From b67d4d92dcd7b5656eb6d027e2fe5c5ee3ab7325 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Mon, 24 Dec 2018 09:08:34 -0500 Subject: [PATCH] Identifier Framework and Message/Event Processing Overhaul (#455) * WIP on new P25 message framer. * WIP on new P25 message framer. * Adds new 1/2 and 3/4 rate trellis decoders. Still trying to chase down why there is a sync pattern mismatch in the NID buffer queue. * WIP - new p25 message framer works correctly now. PDU's are dectected and decoded using the 1/2 and 3/4 rate trellis decoders. Packet Sequences are assembled. Need to add a new packet sequence message factory that can detect Motorola Auto Registration Service (ARS) formatted UDP/ID packets and SNDCP command packets. * WIP - removes nullify on channel results queue. * WIP - updates message framer for TSBK 1/2/3 processing. * WIP removing P25 sync pattern and nid bits from all message parsers. * WIP - merges identifier framework branch changes into this branch. * WIP on updating link control words to new format and identifier framework. * WIP - implements rest of link control words for extended formats * WIP - initial work on ISP/OSP TSBK message restructuring. * WIP - adds more TSBK ISP and OSP messages * WIP - finished updating most of the TSBK ISP and OSP messages. Converted all messages to no longer include the NID (nac + dataunitID) so that each message can simply hold the parsed values. Removed AliasList as an argument to each message and updated all messges to use the new IIdentifer and IAPCO25Channel interfaces. Removed legacy TDU and LDU link control messages and replaced with a single LinkControlWord class hierarchy. P25 decoder state is not yet updated for the new message structure. PDU message parsing is not fully implemented. * WIP - adds some Multi-Block trunking control messages with Alternate and Unconfirmed formats. * WIP - Fully implements AMBTC and UMBTC PDU message parsers. Some package restructuring. * WIP - Resolves Link control parsing in LDU1 and 2 messages. * WIP - finishes updating TSBK and Link Control messages. Adds support for moto link control messages for patch groups. * WIP - finishes updating TSBK and Link Control messages. Adds support for moto link control messages for patch groups. Adds support for moto ARS service, IPV4 decoding, and UDP/IP packets. * WIP - finishes SNDCP packet data with header parsing. Compressed IP or UDP headers are not yet supported, but are logged as such. * WIP - converts all talkgroup identifiers to integers and updates alias list to create a per-protocol map of talkgroup id's to alias. Inserts a legacy talkgroup (ltr, p25, passport, mpt1327, mdc, etc.) conversion utility to convert existing legacy (string) talkgroups to new integer talkgroup format. Creates an audio packet manager to insert metadata into audio packets before streaming packets for playback, recording, and/or streaming. Creates identifier collection to manage current identifiers in each of the channel and decoder states. Creates simplified channel metadata to maintain the now playing display. Moves channel attributes (alias list, system, site, name) as configuration identifiers in the channel state. Many other refactoring and updates. * WIP - numerous updates to push use of identifier collection throughout the system. Updates the channel metadata and audio packet processing logic. Implements a large portion of the P25 decode state processing. Updates traffic channel management. Adds user preferences for timestamp and identifier formatting, however doesn't add a user interface yet. * WIP - initial implementation of user preferences editor window. * WIP - updates preferences editor window to close and dispose window but retain javafx event thread running. * WIP - fully updates p25 decoder state and traffic channel processing to use the new identifier framework. Cleans up channel event processing and removes channel model as the sole broadcaster of channel events. Channel processing manager now listens for all start/stop events and requestors dispatch event directly to the channel processing manager. Resolves issue in airspy that was causing user interface to lock-up. The issue appears to occur when the dongle is plugged into a usb port that can't fully support the data rate. sdrtrunk now recognizes these errors and warns the user that they should use another usb port. * WIP - updates alias action manager and adds new talkgroup range alias identifier. Updates playlist to incorporate a version number so that the playlist updater only runs when needed, to update a playlist to the correct version format. Updates user preferences to use the google event bus to broadcast preference update notifications to remove the need for listeners to register directly on the user preferences instance. * WIP - updates alias id editors to flag invalid values and moves legacy alias ids to legacy package. Updates event logging configuration to add traffic channel decode events and decoded message logging. Removes binary message logging since that is now replaced with demodulated bitstream recording. * Removes system properties from user preferences. Preferences are stored in the OS-dependent settings store (windows - registry) * Updates MapService to use new PlottableDecodeEvent instead of monitoring all of the decoded messages. Updates Fleetsync decoder and related messages to identifier framework. * Updates LTR-Net, Fleetsync and LJ1200 decoders to new identifier framework and adds support for user preferences for formatting of LTR-Net and fleetsync talkgroup identifiers. Breaks LTR-Net messages into various message types and uses a new message factory. Removes alias list from message processing for ltr-net, fleetsync and lj1200. * Updates LTR-Standard messages to Identifier framework. * Removes Plottable and IPlottable from codebase. * wip * WIP on MPT-1327 messages. * Updates MPT1327 decoder to identifier framework and new traffic channel manager. This decoder still needs to be updated with a new message parser factory and a sync detector like what is now used for P25. * Updates MDC1200 decoder and message to identifier framework. * Updates Tait1200 and Passport decoders to identifier framework. * Merges changes from master branch. * Removes legacy alias support classes. Updates passport message processor to only pass valid messages. Adds support to playlist updater for converting passport mobile ids to become passport talkgroup values. * Updates P25 decoder state and channel state to better track allocated traffic channels. Resolves issue in timestamp manager for trackig message timestamps. Resolves issue in airspy tuner that was causing the tuner to stop sending samples to the application. * Update P25 Decoder state to process SNDCP management messages and PDU messages. Updates P25 activity summary to display network and site details. Resolves issue with overlay panel that prevented enabled channels from rendering correctly when zoomed in. * Creates a new FilePreferences as part of the user preferences to begin the transition of SystemProperties over to user preferences and consolidate file preferences in one location. Updates playlist manager to use the new file preferences and to use default.xml as the new default playlist name, following conversion of a playlist_v2.xml format to the new format. In the near future, the user will be able to edit file locations and to create/use other playlists. Resolved an issue with all decoder states where not all identifiers were being included in decode events. --- build.gradle | 1 + gradle/wrapper/gradle-wrapper.properties | 5 +- sdr-trunk.ipr | 735 ++- .../java/io/github/dsheirer/alias/Alias.java | 70 +- .../github/dsheirer/alias/AliasFactory.java | 549 +- .../io/github/dsheirer/alias/AliasList.java | 487 +- .../io/github/dsheirer/alias/AliasModel.java | 98 +- .../dsheirer/alias/PatchGroupAlias.java | 379 -- .../dsheirer/alias/action/AliasAction.java | 6 +- .../alias/action/AliasActionManager.java | 85 +- .../alias/action/RecurringAction.java | 72 +- .../alias/action/beep/BeepAction.java | 24 +- .../alias/action/clip/ClipAction.java | 24 +- .../alias/action/script/ScriptAction.java | 30 +- .../io/github/dsheirer/alias/id/AliasID.java | 52 +- .../github/dsheirer/alias/id/AliasIDType.java | 92 +- .../alias/id/AliasIdentifierEditor.java | 648 +- .../io/github/dsheirer/alias/id/esn/Esn.java | 8 +- .../{ => legacy}/fleetsync/FleetsyncID.java | 40 +- .../fleetsync/FleetsyncIDEditor.java | 46 +- .../alias/id/{ => legacy}/mdc/MDC1200ID.java | 34 +- .../alias/id/legacy/mdc/MDC1200IDEditor.java | 133 + .../id/{ => legacy}/mpt1327/MPT1327ID.java | 34 +- .../{ => legacy}/mpt1327/MPT1327IDEditor.java | 46 +- .../legacy/nonrecordable/NonRecordable.java | 53 + .../nonrecordable/NonRecordableEditor.java | 78 + .../talkgroup/LegacyTalkgroupID.java} | 46 +- .../talkgroup/LegacyTalkgroupIDEditor.java | 121 + .../alias/id/lojack/LoJackFunctionAndID.java | 5 +- .../alias/id/mdc/MDC1200IDEditor.java | 127 - .../dsheirer/alias/id/mobileID/Min.java | 2 +- .../alias/id/nonrecordable/NonRecordable.java | 33 - .../id/nonrecordable/NonRecordableEditor.java | 74 - .../dsheirer/alias/id/record/Record.java | 56 + .../alias/id/record/RecordEditor.java | 73 + .../dsheirer/alias/id/siteID/SiteID.java | 2 +- .../alias/id/talkgroup/Talkgroup.java | 107 + .../alias/id/talkgroup/TalkgroupEditor.java | 164 + .../alias/id/talkgroup/TalkgroupIDEditor.java | 116 - .../alias/id/talkgroup/TalkgroupRange.java | 129 + .../id/talkgroup/TalkgroupRangeEditor.java | 185 + .../dsheirer/audio/AbstractAudioModule.java | 111 + .../audio/AudioMetadataProcessor.java | 79 + .../io/github/dsheirer/audio/AudioModule.java | 55 +- .../dsheirer/audio/AudioPacketManager.java | 98 + .../dsheirer/audio/IAudioController.java | 2 +- .../dsheirer/audio/InversionFrequency.java | 59 - .../audio/broadcast/AudioBroadcaster.java | 10 +- .../audio/broadcast/AudioRecording.java | 24 +- .../audio/broadcast/BroadcastFactory.java | 12 +- .../audio/broadcast/BroadcastModel.java | 38 +- .../broadcast/IBroadcastMetadataUpdater.java | 6 +- .../audio/broadcast/StreamManager.java | 9 +- .../icecast/IcecastAudioBroadcaster.java | 7 +- .../IcecastBroadcastMetadataUpdater.java | 65 +- .../icecast/IcecastHTTPAudioBroadcaster.java | 5 +- .../icecast/IcecastTCPAudioBroadcaster.java | 5 +- .../v1/ShoutcastV1AudioBroadcaster.java | 8 +- .../ShoutcastV1BroadcastMetadataUpdater.java | 57 +- .../v2/ShoutcastV2AudioBroadcaster.java | 57 +- .../dsheirer/audio/invert/AudioInverter.java | 1 - .../audio/invert/InversionFrequency.java | 61 + .../{ => playback}/AudioChannelPanel.java | 184 +- .../{ => playback}/AudioChannelsPanel.java | 42 +- .../{output => playback}/AudioOutput.java | 38 +- .../audio/{ => playback}/AudioPanel.java | 34 +- .../AudioPlaybackManager.java} | 71 +- .../{output => playback}/MonoAudioOutput.java | 2 +- .../StereoAudioOutput.java | 2 +- .../github/dsheirer/bits/BinaryMessage.java | 1050 ++-- .../dsheirer/bits/CorrectedBinaryMessage.java | 10 + .../dsheirer/bits/IBinarySymbolProcessor.java | 28 +- .../github/dsheirer/bits/MessageFramer.java | 61 +- .../bits/MultiSyncPatternMatcher.java | 10 +- .../dsheirer/bits/SoftSyncDetector.java | 26 +- .../io/github/dsheirer/bits/SyncDetector.java | 2 +- .../dsheirer/channel/IChannelDescriptor.java | 67 + .../channel/metadata/AliasedIdentifier.java | 111 - .../AliasedIntegerAttributeMonitor.java | 101 - .../AliasedStringAttributeMonitor.java | 121 - .../channel/metadata/AttributeMonitor.java | 159 - .../channel/metadata/ChannelMetadata.java | 336 + .../metadata/ChannelMetadataField.java | 34 + .../metadata/ChannelMetadataModel.java | 254 +- .../metadata/ChannelMetadataPanel.java | 206 +- .../metadata/ChannelMetadataViewer.java | 20 +- .../IChannelMetadataUpdateListener.java | 29 + .../dsheirer/channel/metadata/Metadata.java | 664 -- .../channel/metadata/MutableMetadata.java | 294 - .../metadata/MutableMetadataChangeEvent.java | 53 - .../state/AlwaysUnsquelchedDecoderState.java | 44 +- .../dsheirer/channel/state/ChannelState.java | 288 +- .../dsheirer/channel/state/DecoderState.java | 181 +- .../channel/state/DecoderStateEvent.java | 3 +- .../state/IDecoderStateEventProvider.java | 4 +- .../TrafficChannelAllocationEvent.java | 45 - .../traffic/TrafficChannelManager.java | 391 -- .../dsheirer/controller/ControllerPanel.java | 27 +- .../dsheirer/controller/channel/Channel.java | 54 +- .../channel/ChannelAutoStartFrame.java | 60 +- .../controller/channel/ChannelController.java | 455 +- .../controller/channel/ChannelEditor.java | 48 +- .../controller/channel/ChannelEvent.java | 184 +- .../controller/channel/ChannelGrantEvent.java | 64 + .../controller/channel/ChannelModel.java | 69 +- .../channel/ChannelProcessingManager.java | 255 +- .../channel/ChannelSelectionManager.java | 124 +- .../controller/channel/ChannelUtils.java | 213 +- .../channel/IChannelEventBroadcaster.java | 29 - .../channel/IChannelEventProvider.java | 4 +- .../channel/TrafficChannelEvent.java | 34 - .../io/github/dsheirer/dsp/NRZDecoder.java | 30 +- .../dsheirer/dsp/afsk/AFSK1200Decoder.java | 28 +- .../ContinuousBufferProcessor.java | 25 +- .../ContinuousReusableBufferProcessor.java | 47 + .../channelizer/PolyphaseChannelSource.java | 30 +- .../channelizer/SampleTimestampManager.java | 27 +- .../output/ChannelOutputProcessor.java | 1 - .../github/dsheirer/dsp/fsk/LTRDecoder.java | 32 +- .../dsp/fsk/SynchronizationMonitor.java | 2 +- .../dsp/psk/pll/AdaptivePLLGainMonitor.java | 2 +- .../symbol/BinaryToByteBufferAssembler.java | 120 + .../io/github/dsheirer/dsp/symbol/Dibit.java | 245 +- .../dsp/symbol/ISyncDetectListener.java | 11 +- .../dsheirer/edac/BerlekempMassey_63.java | 4 +- .../java/io/github/dsheirer/edac/CRC.java | 141 +- .../java/io/github/dsheirer/edac/CRCP25.java | 873 +-- .../io/github/dsheirer/edac/CRCPassport.java | 347 +- .../java/io/github/dsheirer/edac/CRCUtil.java | 4 +- .../java/io/github/dsheirer/edac/Golay18.java | 39 +- .../java/io/github/dsheirer/edac/Golay24.java | 250 +- .../io/github/dsheirer/edac/Hamming10.java | 207 +- .../dsheirer/edac/ReedSolomon_63_47_17.java | 64 +- .../{DMRNode.java => DMR_3_4_Node.java} | 4 +- .../dsheirer/edac/trellis/P25_1_2_Node.java | 85 + .../dsheirer/edac/trellis/P25_3_4_Node.java | 84 + .../edac/trellis/ViterbiDecoder_1_2_P25.java | 154 + .../edac/trellis/ViterbiDecoder_3_4_DMR.java | 8 +- .../edac/trellis/ViterbiDecoder_3_4_P25.java | 160 + .../github/dsheirer/eventbus/MyEventBus.java | 36 + .../dsheirer/filter/FilterEditorPanel.java | 697 ++- .../dsheirer/gui/JavaFxWindowManager.java | 96 + .../java/io/github/dsheirer/gui/SDRTrunk.java | 78 +- .../chart/IInstrumentedAFSK1200Decoder.java | 8 +- .../decoder/AbstractAFSK1200Pane.java | 12 +- .../gui/instrument/decoder/LJ1200Pane.java | 4 +- .../gui/instrument/decoder/LTRNetPane.java | 12 +- .../gui/instrument/decoder/MDC1200Pane.java | 2 +- .../gui/instrument/decoder/MPT1327Pane.java | 4 +- .../instrument/decoder/P25Phase1C4FMPane.java | 20 +- .../instrument/decoder/P25Phase1LSMPane.java | 18 +- .../gui/instrument/decoder/Tait1200Pane.java | 4 +- .../DecodeEventViewPreferenceEditor.java | 123 + .../preference/PreferenceEditorFactory.java | 39 + .../gui/preference/PreferenceEditorType.java | 38 + .../PreferenceEditorViewRequest.java | 35 + .../gui/preference/PreferencesEditor.java | 291 + .../TalkgroupFormatPreferenceEditor.java | 120 + .../io/github/dsheirer/identifier/Form.java | 54 + .../dsheirer/identifier/Identifier.java | 125 + .../dsheirer/identifier/IdentifierClass.java | 34 + .../identifier/IdentifierCollection.java | 296 + .../identifier/IdentifierUpdateListener.java | 30 + .../IdentifierUpdateNotification.java | 87 + .../identifier/IdentifierUpdateProvider.java | 29 + .../MutableIdentifierCollection.java | 399 ++ .../io/github/dsheirer/identifier/Role.java | 29 + .../AliasListConfigurationIdentifier.java | 45 + ...nnelDescriptorConfigurationIdentifier.java | 57 + .../ChannelNameConfigurationIdentifier.java | 50 + .../ConfigurationLongIdentifier.java | 44 + .../ConfigurationStringIdentifier.java | 44 + .../DecoderTypeConfigurationIdentifier.java | 56 + .../FrequencyConfigurationIdentifier.java | 39 + .../SiteConfigurationIdentifier.java | 50 + .../SystemConfigurationIdentifier.java | 50 + .../DecoderLogicalChannelNameIdentifier.java | 52 + .../decoder/DecoderStateIdentifier.java | 66 + .../identifier/encryption/EncryptionKey.java | 93 + .../encryption/EncryptionKeyIdentifier.java | 36 + .../identifier/esn/ESNIdentifier.java | 66 + .../identifier/integer/IntegerIdentifier.java | 66 + .../dsheirer/identifier/ipv4/IPV4Address.java | 93 + .../identifier/ipv4/IPV4Identifier.java | 34 + .../identifier/longnumber/LongIdentifier.java | 66 + .../dsheirer/identifier/patch/PatchGroup.java | 101 + .../patch/PatchGroupIdentifier.java | 34 + .../identifier/patch/PatchGroupManager.java | 184 + .../identifier/string/StringIdentifier.java | 65 + .../identifier/talkgroup/LTRTalkgroup.java | 79 + .../talkgroup/TalkgroupIdentifier.java | 51 + .../map/IPlottableUpdateListener.java | 27 + .../java/io/github/dsheirer/map/MapPanel.java | 26 +- .../io/github/dsheirer/map/MapService.java | 144 +- .../io/github/dsheirer/map/Plottable.java | 138 - .../github/dsheirer/map/PlottableEntity.java | 170 - .../dsheirer/map/PlottableEntityHistory.java | 82 + .../dsheirer/map/PlottableEntityPainter.java | 29 +- .../dsheirer/map/PlottableEntityRenderer.java | 174 +- .../dsheirer/map/PlottableUpdateListener.java | 27 - .../dsheirer/message/IBitErrorProvider.java | 33 + .../io/github/dsheirer/message/IMessage.java | 46 + .../dsheirer/message/IMessageListener.java | 2 +- .../dsheirer/message/IMessageProvider.java | 4 +- .../io/github/dsheirer/message/Message.java | 157 +- .../dsheirer/message/SyncLossMessage.java | 78 + .../dsheirer/module/ProcessingChain.java | 199 +- .../dsheirer/module/decode/Decoder.java | 13 +- .../module/decode/DecoderFactory.java | 183 +- .../dsheirer/module/decode/DecoderType.java | 59 +- .../decode/event/ActivitySummaryProvider.java | 3 +- .../module/decode/event/CallEvent.java | 43 +- .../event/CallEventAliasCellRenderer.java | 70 - .../module/decode/event/CallEventModel.java | 220 - .../module/decode/event/CallEventPanel.java | 75 - .../module/decode/event/DecodeEvent.java | 297 + .../module/decode/event/DecodeEventModel.java | 222 + .../module/decode/event/DecodeEventPanel.java | 411 ++ .../module/decode/event/DecodeEventType.java | 85 + .../decode/event/ICallEventListener.java | 8 - .../decode/event/ICallEventProvider.java | 9 - .../module/decode/event/IDecodeEvent.java | 74 + .../decode/event/IDecodeEventListener.java | 28 + .../decode/event/IDecodeEventProvider.java | 9 + .../decode/event/MessageActivityModel.java | 273 +- .../decode/event/MessageActivityPanel.java | 21 +- .../module/decode/event/MessageItem.java | 97 + .../decode/event/PlottableDecodeEvent.java | 239 + .../decode/fleetsync1/Fleetsync1Message.java | 201 - .../Fleetsync1MessageProcessor.java | 51 - .../decode/fleetsync2/Fleetsync2Decoder.java | 47 +- .../Fleetsync2DecoderInstrumented.java | 2 +- .../fleetsync2/Fleetsync2DecoderState.java | 264 +- .../Fleetsync2MessageProcessor.java | 133 +- .../decode/fleetsync2/FleetsyncCallEvent.java | 206 - .../decode/fleetsync2/FleetsyncMessage.java | 894 --- .../fleetsync2/FleetsyncMessageFilter.java | 91 +- .../identifier/FleetsyncIdentifier.java | 88 + .../identifier/FleetsyncStatus.java | 59 + .../AutomaticNumberIdentification.java | 93 + .../fleetsync2/message/Fleetsync2Message.java | 304 + .../fleetsync2/message/LocationReport.java | 250 + .../decode/fleetsync2/message/Status.java | 110 + .../dsheirer/module/decode/ip/Header.java | 74 + .../dsheirer/module/decode/ip/IHeader.java | 39 + .../{p25/reference => ip}/IPProtocol.java | 22 +- .../dsheirer/module/decode/ip/IPacket.java | 43 + .../dsheirer/module/decode/ip/Packet.java | 62 + .../decode/ip/PacketMessageFactory.java | 108 + .../ip/UnknownCompressedHeaderPacket.java | 90 + .../module/decode/ip/UnknownPacket.java | 81 + .../module/decode/ip/ars/ARSHeader.java | 144 + .../decode/ip/ars/ARSHeaderFactory.java | 50 + .../module/decode/ip/ars/ARSPDUType.java | 63 + .../module/decode/ip/ars/ARSPacket.java | 67 + .../decode/ip/ars/DeviceDeRegistration.java | 44 + .../decode/ip/ars/DeviceRegistration.java | 153 + .../ip/ars/DeviceRegistrationAcknowledge.java | 44 + .../module/decode/ip/ars/QueryMessage.java | 44 + .../ip/ars/RegistrationAcknowledgement.java | 96 + .../decode/ip/ars/UserDeRegistration.java | 44 + .../ip/ars/UserDeRegistrationAcknowledge.java | 44 + .../decode/ip/ars/UserRegistration.java | 140 + .../ip/ars/UserRegistrationAcknowledge.java | 88 + .../module/decode/ip/ipv4/IPV4Header.java | 161 + .../module/decode/ip/ipv4/IPV4Packet.java | 112 + .../module/decode/ip/udp/UDPHeader.java | 78 + .../module/decode/ip/udp/UDPPacket.java | 88 + .../decode/lj1200/FunctionAndReplyCode.java | 52 + .../FunctionAndReplyCodeIdentifier.java | 46 + .../module/decode/lj1200/LJ1200CallEvent.java | 100 +- .../module/decode/lj1200/LJ1200Decoder.java | 45 +- .../lj1200/LJ1200DecoderInstrumented.java | 2 +- .../decode/lj1200/LJ1200DecoderState.java | 60 +- .../module/decode/lj1200/LJ1200Message.java | 552 +- .../decode/lj1200/LJ1200MessageFilter.java | 39 +- .../decode/lj1200/LJ1200MessageProcessor.java | 122 +- .../lj1200/LJ1200TransponderMessage.java | 197 +- .../module/decode/ltrnet/LTRCallEvent.java | 212 - .../module/decode/ltrnet/LTRNetDecoder.java | 9 +- .../ltrnet/LTRNetDecoderInstrumented.java | 5 +- .../decode/ltrnet/LTRNetDecoderState.java | 1164 ++-- .../decode/ltrnet/LTRNetISWMessage.java | 345 -- .../module/decode/ltrnet/LTRNetMessage.java | 243 - .../decode/ltrnet/LTRNetMessageFilter.java | 92 +- .../decode/ltrnet/LTRNetMessageProcessor.java | 228 +- .../decode/ltrnet/LTRNetOSWMessage.java | 644 -- .../decode/ltrnet/LtrNetMessageType.java | 46 + .../decode/ltrnet/channel/LtrNetChannel.java | 111 + .../ltrnet/identifier/NeighborIdentifier.java | 46 + .../ltrnet/identifier/SiteIdentifier.java | 46 + .../ltrnet/identifier/UniqueIdentifier.java | 66 + .../decode/ltrnet/message/LtrNetMessage.java | 116 + .../ltrnet/message/LtrNetMessageFactory.java | 119 + .../decode/ltrnet/message/isw/IswCallEnd.java | 80 + .../ltrnet/message/isw/IswCallStart.java | 80 + .../ltrnet/message/isw/IswUniqueId.java | 83 + .../decode/ltrnet/message/isw/IswUnknown.java | 65 + .../ltrnet/message/isw/LtrNetIswMessage.java | 93 + .../isw/RegistrationRequestEsnHigh.java | 113 + .../isw/RegistrationRequestEsnLow.java | 112 + .../ltrnet/message/isw/RequestAccess.java | 72 + .../ltrnet/message/osw/ChannelMapHigh.java | 77 + .../ltrnet/message/osw/ChannelMapLow.java | 77 + .../decode/ltrnet/message/osw/Frequency.java | 63 + .../ltrnet/message/osw/FrequencyHigh.java | 80 + .../ltrnet/message/osw/FrequencyLow.java | 77 + .../ltrnet/message/osw/LtrNetOswMessage.java | 109 + .../decode/ltrnet/message/osw/NeighborId.java | 87 + .../decode/ltrnet/message/osw/OswCallEnd.java | 80 + .../ltrnet/message/osw/OswCallStart.java | 80 + .../decode/ltrnet/message/osw/OswUnknown.java | 65 + .../message/osw/ReceiveFrequencyHigh.java | 63 + .../message/osw/ReceiveFrequencyLow.java | 60 + .../message/osw/RegistrationAccept.java | 82 + .../decode/ltrnet/message/osw/SiteId.java | 82 + .../decode/ltrnet/message/osw/SystemIdle.java | 65 + .../message/osw/TransmitFrequencyHigh.java | 60 + .../message/osw/TransmitFrequencyLow.java | 60 + .../ltrstandard/LTRStandardDecoder.java | 2 +- .../ltrstandard/LTRStandardDecoderState.java | 238 +- .../ltrstandard/LTRStandardMessageFilter.java | 86 +- .../LTRStandardMessageProcessor.java | 197 +- .../ltrstandard/LtrStandardMessageType.java | 29 + .../ltrstandard/channel/LtrChannel.java | 105 + .../decode/ltrstandard/message/Call.java | 67 + .../decode/ltrstandard/message/CallEnd.java | 67 + .../ltrstandard/message/CallEndMessage.java | 67 - .../ltrstandard/message/CallMessage.java | 67 - .../decode/ltrstandard/message/Idle.java | 64 + .../ltrstandard/message/IdleMessage.java | 58 - .../message/LTRStandardMessage.java | 281 +- .../ltrstandard/message/UnknownMessage.java | 91 +- .../module/decode/mdc1200/MDCCallEvent.java | 161 - .../module/decode/mdc1200/MDCDecoder.java | 39 +- .../mdc1200/MDCDecoderInstrumented.java | 28 +- .../decode/mdc1200/MDCDecoderState.java | 137 +- .../module/decode/mdc1200/MDCMessage.java | 448 +- .../decode/mdc1200/MDCMessageFilter.java | 69 +- .../decode/mdc1200/MDCMessageProcessor.java | 274 +- .../mdc1200/identifier/MDC1200Identifier.java | 62 + .../decode/mpt1327/MPT1327CallEvent.java | 154 - .../mpt1327/MPT1327ChannelGrantEvent.java | 142 + .../module/decode/mpt1327/MPT1327Decoder.java | 77 +- .../mpt1327/MPT1327DecoderInstrumented.java | 28 +- .../decode/mpt1327/MPT1327DecoderState.java | 359 +- .../decode/mpt1327/MPT1327IdentType.java | 25 + .../module/decode/mpt1327/MPT1327Message.java | 3730 ++++++----- .../decode/mpt1327/MPT1327MessageFilter.java | 92 +- .../mpt1327/MPT1327MessageProcessor.java | 92 +- .../mpt1327/MPT1327TrafficChannelManager.java | 390 ++ .../mpt1327/channel/MPT1327Channel.java | 142 + .../decode/mpt1327/identifier/IdentType.java | 91 + .../identifier/MPT1327SiteIdentifier.java | 42 + .../mpt1327/identifier/MPT1327Talkgroup.java | 105 + .../decode/p25/IDataUnitDetectListener.java | 42 + .../module/decode/p25/P25CallEvent.java | 170 - .../decode/p25/P25ChannelGrantEvent.java | 172 + .../decode/p25/P25ChannelStatusProcessor.java | 85 + .../decode/p25/P25DataUnitDetector.java | 557 ++ .../module/decode/p25/P25DecodeEvent.java | 49 + .../module/decode/p25/P25Decoder.java | 12 +- .../module/decode/p25/P25DecoderC4FM.java | 43 +- .../p25/P25DecoderC4FMInstrumented.java | 6 +- .../module/decode/p25/P25DecoderLSM.java | 45 +- .../decode/p25/P25DecoderLSMInstrumented.java | 6 +- .../module/decode/p25/P25DecoderState.java | 5519 +++++++---------- .../module/decode/p25/P25Interleave.java | 224 +- .../module/decode/p25/P25MessageFramer.java | 744 --- .../module/decode/p25/P25MessageFramer2.java | 549 ++ .../decode/p25/P25MessageProcessor.java | 143 +- .../module/decode/p25/P25Metadata.java | 37 + .../module/decode/p25/P25SyncDetector.java | 163 + .../decode/p25/P25TrafficChannelManager.java | 514 ++ .../dsheirer/module/decode/p25/P25Utils.java | 62 + .../module/decode/p25/PatchGroup.java | 102 - .../module/decode/p25/PatchGroupManager.java | 245 - .../module/decode/p25/Trellis_1_2_Rate.java | 533 -- .../module/decode/p25/Trellis_3_4_Rate.java | 661 -- .../decode/p25/audio/P25AudioModule.java | 79 +- .../decode/p25/identifier/APCO25Lra.java | 49 + .../decode/p25/identifier/APCO25Nac.java | 55 + .../decode/p25/identifier/APCO25Rfss.java | 49 + .../decode/p25/identifier/APCO25Site.java | 49 + .../decode/p25/identifier/APCO25System.java | 48 + .../decode/p25/identifier/APCO25Wacn.java | 49 + .../p25/identifier/channel/APCO25Channel.java | 135 + .../channel/APCO25ExplicitChannel.java | 44 + .../p25/identifier/channel/P25Channel.java | 189 + .../channel/P25ExplicitChannel.java | 84 + .../encryption/APCO25EncryptionKey.java | 49 + .../p25/identifier/ipv4/APCO25IpAddress.java | 52 + .../message/APCO25ShortDataMessage.java | 58 + .../identifier/patch/APCO25PatchGroup.java | 46 + .../p25/identifier/status/APCO25Status.java | 69 + .../APCO25AnnouncementTalkgroup.java | 47 + .../talkgroup/APCO25AnyTalkgroup.java | 47 + .../talkgroup/APCO25FromTalkgroup.java | 50 + .../talkgroup/APCO25IdentifierTalkgroup.java | 50 + .../identifier/talkgroup/APCO25Talkgroup.java | 39 + .../talkgroup/APCO25ToTalkgroup.java | 50 + .../telephone/APCO25TelephoneNumber.java | 65 + .../decode/p25/message/IAdjacentSite.java | 28 +- .../decode/p25/message/IBandIdentifier.java | 64 - .../decode/p25/message/IFrequencyBand.java | 65 + .../p25/message/IFrequencyBandReceiver.java | 37 + .../p25/message/IdentifierReceiver.java | 14 - .../module/decode/p25/message/P25Message.java | 207 +- .../decode/p25/message/P25MessageFactory.java | 79 + .../decode/p25/message/UnknownP25Message.java | 63 + .../message/filter/AMBTCMessageFilter.java | 55 + .../p25/message/filter/HDUMessageFilter.java | 30 +- .../message/filter/IPPacketMessageFilter.java | 55 + .../p25/message/filter/LDUMessageFilter.java | 30 +- .../message/filter/P25MessageFilterSet.java | 40 +- .../p25/message/filter/PDUMessageFilter.java | 66 +- .../message/filter/SNDCPMessageFilter.java | 55 + .../message/filter/SyncLossMessageFilter.java | 55 + .../message/filter/TDULCMessageFilter.java | 32 +- .../p25/message/filter/TDUMessageFilter.java | 30 +- .../message/filter/TSBKMessageFilterSet.java | 204 +- .../message/filter/UMBTCMessageFilter.java | 55 + .../decode/p25/message/hdu/HDUMessage.java | 427 +- .../decode/p25/message/hdu/HeaderData.java | 175 + .../p25/message/lc/LinkControlOpcode.java | 155 + .../p25/message/lc/LinkControlWord.java | 164 + .../message/lc/LinkControlWordFactory.java | 148 + .../message/lc/UnknownLinkControlWord.java | 39 + .../lc/motorola/LCMotorolaPatchGroupAdd.java | 144 + .../motorola/LCMotorolaPatchGroupDelete.java | 143 + ...CMotorolaPatchGroupVoiceChannelUpdate.java | 132 + .../LCMotorolaPatchGroupVoiceChannelUser.java | 118 + .../lc/motorola/LCMotorolaTalkComplete.java | 115 + .../lc/motorola/LCMotorolaUnknownOpcode.java | 58 + .../lc/motorola/MotorolaLinkControlWord.java | 37 + .../LCAdjacentSiteStatusBroadcast.java | 168 + ...LCAdjacentSiteStatusBroadcastExplicit.java | 157 + .../p25/message/lc/standard/LCCallAlert.java | 107 + .../lc/standard/LCCallTermination.java | 96 + .../standard/LCExtendedFunctionCommand.java | 109 + .../lc/standard/LCFrequencyBandUpdate.java | 148 + .../LCFrequencyBandUpdateExplicit.java | 160 + .../lc/standard/LCGroupAffiliationQuery.java | 106 + .../standard/LCGroupVoiceChannelUpdate.java | 146 + .../LCGroupVoiceChannelUpdateExplicit.java | 126 + .../lc/standard/LCGroupVoiceChannelUser.java | 122 + .../message/lc/standard/LCMessageUpdate.java | 121 + .../lc/standard/LCNetworkStatusBroadcast.java | 140 + .../LCNetworkStatusBroadcastExplicit.java | 127 + .../LCProtectionParameterBroadcast.java | 104 + .../lc/standard/LCRFSSStatusBroadcast.java | 168 + .../LCRFSSStatusBroadcastExplicit.java | 155 + .../LCSecondaryControlChannelBroadcast.java | 181 + ...ondaryControlChannelBroadcastExplicit.java | 141 + .../message/lc/standard/LCStatusQuery.java | 106 + .../message/lc/standard/LCStatusUpdate.java | 135 + .../lc/standard/LCSystemServiceBroadcast.java | 91 + .../LCTelephoneInterconnectAnswerRequest.java | 125 + ...TelephoneInterconnectVoiceChannelUser.java | 118 + .../standard/LCUnitAuthenticationCommand.java | 124 + .../standard/LCUnitRegistrationCommand.java | 124 + .../standard/LCUnitToUnitAnswerRequest.java | 124 + .../LCUnitToUnitVoiceChannelUser.java | 123 + .../message/ldu/EncryptionSyncParameters.java | 141 + .../decode/p25/message/ldu/LDU1Message.java | 360 +- .../decode/p25/message/ldu/LDU2Message.java | 353 +- .../decode/p25/message/ldu/LDUMessage.java | 110 +- .../ldu/lc/AdjacentSiteStatusBroadcast.java | 172 - .../AdjacentSiteStatusBroadcastExplicit.java | 183 - .../decode/p25/message/ldu/lc/CallAlert.java | 45 - .../p25/message/ldu/lc/CallTermination.java | 38 - .../ldu/lc/ChannelIdentifierUpdate.java | 118 - .../lc/ChannelIdentifierUpdateExplicit.java | 100 - .../ldu/lc/ExtendedFunctionCommand.java | 86 - .../message/ldu/lc/GroupAffiliationQuery.java | 51 - .../ldu/lc/GroupVoiceChannelUpdate.java | 189 - .../lc/GroupVoiceChannelUpdateExplicit.java | 171 - .../message/ldu/lc/GroupVoiceChannelUser.java | 86 - .../message/ldu/lc/LDULCMessageFactory.java | 95 - .../p25/message/ldu/lc/MessageUpdate.java | 57 - .../ldu/lc/NetworkStatusBroadcast.java | 129 - .../lc/NetworkStatusBroadcastExplicit.java | 148 - .../ldu/lc/ProtectionParameterBroadcast.java | 58 - .../message/ldu/lc/RFSSStatusBroadcast.java | 147 - .../ldu/lc/RFSSStatusBroadcastExplicit.java | 168 - .../lc/SecondaryControlChannelBroadcast.java | 185 - ...ondaryControlChannelBroadcastExplicit.java | 157 - .../p25/message/ldu/lc/StatusQuery.java | 51 - .../p25/message/ldu/lc/StatusUpdate.java | 64 - .../ldu/lc/SystemServiceBroadcast.java | 63 - .../TelephoneInterconnectAnswerRequest.java | 80 - ...TelephoneInterconnectVoiceChannelUser.java | 93 - .../ldu/lc/UnitAuthenticationCommand.java | 69 - .../ldu/lc/UnitRegistrationCommand.java | 70 - .../ldu/lc/UnitToUnitAnswerRequest.java | 87 - .../ldu/lc/UnitToUnitVoiceChannelUser.java | 86 - .../ldu/lc/motorola/MotorolaOpcode_0.java | 51 - .../decode/p25/message/pdu/PDUHeader.java | 193 + .../p25/message/pdu/PDUHeaderFactory.java | 55 + .../decode/p25/message/pdu/PDUMessage.java | 355 +- .../p25/message/pdu/PDUMessageFactory.java | 488 +- .../decode/p25/message/pdu/PDUSequence.java | 161 + .../p25/message/pdu/PDUSequenceMessage.java | 43 + .../pdu/UnitToUnitChannelGrantExtended.java | 220 - .../p25/message/pdu/ambtc/AMBTCHeader.java | 90 + .../p25/message/pdu/ambtc/AMBTCMessage.java | 190 + .../ambtc/isp/AMBTCAuthenticationQuery.java | 147 + .../isp/AMBTCAuthenticationResponse.java | 161 + .../pdu/ambtc/isp/AMBTCCallAlertRequest.java | 142 + .../isp/AMBTCGroupAffiliationRequest.java | 144 + .../AMBTCIndividualDataServiceRequest.java | 154 + .../isp/AMBTCLocationRegistrationRequest.java | 185 + .../ambtc/isp/AMBTCMessageUpdateRequest.java | 152 + .../ambtc/isp/AMBTCRoamingAddressRequest.java | 141 + .../ambtc/isp/AMBTCStatusQueryRequest.java | 144 + .../ambtc/isp/AMBTCStatusQueryResponse.java | 165 + .../ambtc/isp/AMBTCStatusUpdateRequest.java | 165 + .../isp/AMBTCUnitAcknowledgeResponse.java | 159 + ...CUnitToUnitVoiceServiceAnswerResponse.java | 168 + .../AMBTCUnitToUnitVoiceServiceRequest.java | 155 + .../osp/AMBTCAdjacentStatusBroadcast.java | 189 + .../message/pdu/ambtc/osp/AMBTCCallAlert.java | 149 + .../ambtc/osp/AMBTCGroupAffiliationQuery.java | 149 + .../osp/AMBTCGroupAffiliationResponse.java | 241 + .../ambtc/osp/AMBTCGroupDataChannelGrant.java | 166 + .../osp/AMBTCGroupVoiceChannelGrant.java | 174 + .../osp/AMBTCIndividualDataChannelGrant.java | 236 + .../pdu/ambtc/osp/AMBTCMessageUpdate.java | 170 + .../AMBTCProtectionParameterBroadcast.java | 146 + .../osp/AMBTCRoamingAddressResponse.java | 169 + .../ambtc/osp/AMBTCRoamingAddressUpdate.java | 169 + .../pdu/ambtc/osp/AMBTCStatusQuery.java | 149 + .../pdu/ambtc/osp/AMBTCStatusUpdate.java | 182 + ...MBTCTelephoneInterconnectChannelGrant.java | 171 + ...lephoneInterconnectChannelGrantUpdate.java | 171 + .../osp/AMBTCUnitRegistrationResponse.java | 183 + .../osp/AMBTCUnitToUnitAnswerRequest.java | 154 + ...BTCUnitToUnitVoiceServiceChannelGrant.java | 236 + ...tToUnitVoiceServiceChannelGrantUpdate.java | 237 + .../message/pdu/block/ConfirmedDataBlock.java | 89 + .../p25/message/pdu/block/DataBlock.java | 55 + .../pdu/block/UnconfirmedDataBlock.java | 71 + .../pdu/confirmed/PDUConfirmedMessage.java | 207 - .../confirmed/PDUConfirmedMessageFactory.java | 79 - .../message/pdu/confirmed/PDUTypeUnknown.java | 55 - .../p25/message/pdu/confirmed/PacketData.java | 65 - .../SNDCPActivateTDSContextAccept.java | 242 - .../SNDCPActivateTDSContextReject.java | 53 - .../SNDCPActivateTDSContextRequest.java | 148 - .../confirmed/SNDCPDeactivateTDSContext.java | 59 - .../message/pdu/confirmed/SNDCPUserData.java | 355 -- .../AdjacentStatusBroadcastExtended.java | 208 - .../pdu/osp/control/CallAlertExtended.java | 80 - .../GroupAffiliationQueryExtended.java | 80 - .../GroupAffiliationResponseExtended.java | 131 - .../osp/control/MessageUpdateExtended.java | 90 - .../NetworkStatusBroadcastExtended.java | 181 - .../control/ProtectionParameterBroadcast.java | 113 - .../control/RFSSStatusBroadcastExtended.java | 185 - .../control/RoamingAddressUpdateExtended.java | 269 - .../pdu/osp/control/StatusQueryExtended.java | 81 - .../pdu/osp/control/StatusUpdateExtended.java | 97 - .../UnitRegistrationResponseExtended.java | 99 - .../data/GroupDataChannelGrantExtended.java | 199 - .../IndividualDataChannelGrantExtended.java | 22 - .../voice/GroupVoiceChannelGrantExplicit.java | 199 - ...phoneInterconnectChannelGrantExplicit.java | 206 - ...nterconnectChannelGrantUpdateExplicit.java | 22 - .../UnitToUnitAnswerRequestExplicit.java | 112 - .../UnitToUnitVoiceChannelGrantExtended.java | 25 - ...ToUnitVoiceChannelGrantUpdateExtended.java | 228 - .../p25/message/pdu/packet/PacketHeader.java | 128 + .../p25/message/pdu/packet/PacketMessage.java | 267 + .../sndcp/ActivateTdsContextAccept.java | 232 + .../sndcp/ActivateTdsContextReject.java | 77 + .../sndcp/ActivateTdsContextRequest.java | 200 + .../sndcp/DeActivateTdsContextRequest.java | 98 + .../pdu/packet/sndcp/SNDCPMessage.java | 92 + .../pdu/packet/sndcp/SNDCPMessageFactory.java | 58 + .../pdu/packet/sndcp/SNDCPPacketHeader.java | 120 + .../pdu/packet/sndcp/SNDCPPacketMessage.java | 70 + .../message/pdu/response/ResponseHeader.java | 92 + .../message/pdu/response/ResponseMessage.java | 157 + .../p25/message/pdu/umbtc/UMBTCHeader.java | 92 + .../p25/message/pdu/umbtc/UMBTCMessage.java | 149 + ...oneInterconnectRequestExplicitDialing.java | 151 + .../message/tdu/TDULinkControlMessage.java | 199 + .../decode/p25/message/tdu/TDUMessage.java | 74 +- .../tdu/lc/AdjacentSiteStatusBroadcast.java | 170 - .../AdjacentSiteStatusBroadcastExplicit.java | 184 - .../decode/p25/message/tdu/lc/CallAlert.java | 44 - .../p25/message/tdu/lc/CallTermination.java | 37 - .../tdu/lc/ChannelIdentifierUpdate.java | 117 - .../lc/ChannelIdentifierUpdateExplicit.java | 114 - .../tdu/lc/ExtendedFunctionCommand.java | 85 - .../message/tdu/lc/GroupAffiliationQuery.java | 50 - .../tdu/lc/GroupVoiceChannelUpdate.java | 205 - .../lc/GroupVoiceChannelUpdateExplicit.java | 176 - .../message/tdu/lc/GroupVoiceChannelUser.java | 85 - .../p25/message/tdu/lc/MessageUpdate.java | 56 - .../tdu/lc/NetworkStatusBroadcast.java | 128 - .../lc/NetworkStatusBroadcastExplicit.java | 146 - .../tdu/lc/ProtectionParameterBroadcast.java | 57 - .../message/tdu/lc/RFSSStatusBroadcast.java | 146 - .../tdu/lc/RFSSStatusBroadcastExplicit.java | 171 - .../lc/SecondaryControlChannelBroadcast.java | 184 - ...ondaryControlChannelBroadcastExplicit.java | 156 - .../p25/message/tdu/lc/StatusQuery.java | 50 - .../p25/message/tdu/lc/StatusUpdate.java | 63 - .../tdu/lc/SystemServiceBroadcast.java | 62 - .../message/tdu/lc/TDULCMessageFactory.java | 95 - .../message/tdu/lc/TDULinkControlMessage.java | 282 - .../TelephoneInterconnectAnswerRequest.java | 79 - ...TelephoneInterconnectVoiceChannelUser.java | 92 - .../tdu/lc/UnitAuthenticationCommand.java | 70 - .../tdu/lc/UnitRegistrationCommand.java | 69 - .../tdu/lc/UnitToUnitAnswerRequest.java | 85 - .../tdu/lc/UnitToUnitVoiceChannelUser.java | 85 - .../decode/p25/message/tsbk/ChannelGrant.java | 110 - .../p25/message/tsbk/GroupChannelGrant.java | 93 - .../tsbk/GroupChannelGrantExplicit.java | 155 - .../message/tsbk/GroupMultiChannelGrant.java | 220 - .../decode/p25/message/tsbk/ISPMessage.java | 30 + .../decode/p25/message/tsbk/OSPMessage.java | 30 + .../decode/p25/message/tsbk/Opcode.java | 296 + .../p25/message/tsbk/ServiceMessage.java | 43 - .../decode/p25/message/tsbk/TSBKMessage.java | 258 +- .../p25/message/tsbk/TSBKMessageFactory.java | 491 +- .../p25/message/tsbk/UnitChannelGrant.java | 90 - ...ntrolChannelBaseStationIdentification.java | 145 - .../message/tsbk/motorola/MotorolaOpcode.java | 111 - .../tsbk/motorola/MotorolaTSBKMessage.java | 41 - .../p25/message/tsbk/motorola/PatchGroup.java | 92 - .../message/tsbk/motorola/PatchGroupAdd.java | 20 - .../tsbk/motorola/PatchGroupDelete.java | 20 - .../motorola/PatchGroupVoiceChannelGrant.java | 187 - .../PatchGroupVoiceChannelGrantUpdate.java | 197 - .../PlannedControlChannnelShutdown.java | 30 - .../message/tsbk/motorola/SystemLoading.java | 33 - ...afficChannelBaseStationIdentification.java | 65 - .../isp/UnknownMotorolaISPMessage.java | 58 + .../tsbk/motorola/osp/ChannelCWID.java | 137 + .../tsbk/motorola/osp/ChannelLoading.java | 58 + .../tsbk/motorola/osp/PatchGroupAdd.java | 179 + .../tsbk/motorola/osp/PatchGroupDelete.java | 182 + .../osp/PatchGroupVoiceChannelGrant.java | 132 + .../PatchGroupVoiceChannelGrantUpdate.java | 153 + .../motorola/osp/PlannedChannelShutdown.java | 58 + .../osp/UnknownMotorolaOSPMessage.java | 58 + .../tsbk/osp/control/AcknowledgeResponse.java | 121 - .../osp/control/AdjacentStatusBroadcast.java | 216 - .../osp/control/AuthenticationCommand.java | 77 - .../message/tsbk/osp/control/CallAlert.java | 62 - .../tsbk/osp/control/DenyResponse.java | 95 - .../osp/control/ExtendedFunctionCommand.java | 87 - .../osp/control/GroupAffiliationQuery.java | 63 - .../osp/control/GroupAffiliationResponse.java | 86 - .../tsbk/osp/control/IdentifierUpdate.java | 83 - .../osp/control/IdentifierUpdateNonVUHF.java | 77 - .../osp/control/IdentifierUpdateTDMA.java | 186 - .../osp/control/IdentifierUpdateVUHF.java | 89 - .../control/LocationRegistrationResponse.java | 78 - .../tsbk/osp/control/MessageUpdate.java | 70 - .../osp/control/NetworkStatusBroadcast.java | 147 - .../control/ProtectionParameterUpdate.java | 66 - .../tsbk/osp/control/QueuedResponse.java | 96 - .../tsbk/osp/control/RFSSStatusBroadcast.java | 158 - .../osp/control/RadioUnitMonitorCommand.java | 69 - .../osp/control/RoamingAddressCommand.java | 72 - .../SecondaryControlChannelBroadcast.java | 266 - ...ondaryControlChannelBroadcastExplicit.java | 57 - .../message/tsbk/osp/control/StatusQuery.java | 62 - .../tsbk/osp/control/StatusUpdate.java | 76 - .../tsbk/osp/control/SyncBroadcast.java | 274 - .../tsbk/osp/control/SystemService.java | 87 - .../osp/control/SystemServiceBroadcast.java | 64 - .../osp/control/TimeAndDateAnnouncement.java | 156 - .../UnitDeregistrationAcknowledge.java | 64 - .../osp/control/UnitRegistrationCommand.java | 62 - .../osp/control/UnitRegistrationResponse.java | 71 - .../data/GroupDataChannelAnnouncement.java | 23 - .../GroupDataChannelAnnouncementExplicit.java | 23 - .../tsbk/osp/data/GroupDataChannelGrant.java | 40 - .../osp/data/IndividualDataChannelGrant.java | 23 - .../p25/message/tsbk/osp/data/SNDCPData.java | 29 - .../SNDCPDataChannelAnnouncementExplicit.java | 178 - .../tsbk/osp/data/SNDCPDataChannelGrant.java | 164 - .../tsbk/osp/data/SNDCPDataPageRequest.java | 51 - .../osp/voice/GroupVoiceChannelGrant.java | 24 - .../voice/GroupVoiceChannelGrantUpdate.java | 23 - .../GroupVoiceChannelGrantUpdateExplicit.java | 23 - .../TelephoneInterconnectAnswerRequest.java | 85 - ...elephoneInterconnectVoiceChannelGrant.java | 66 - ...neInterconnectVoiceChannelGrantUpdate.java | 23 - .../osp/voice/UnitToUnitAnswerRequest.java | 23 - .../voice/UnitToUnitVoiceChannelGrant.java | 47 - .../UnitToUnitVoiceChannelGrantUpdate.java | 22 - .../standard/isp/AuthenticationQuery.java | 77 + .../tsbk/standard/isp/CallAlertRequest.java | 77 + .../standard/isp/CancelServiceRequest.java | 104 + .../standard/isp/EmergencyAlarmRequest.java | 77 + .../isp/ExtendedFunctionResponse.java | 88 + .../isp/FrequencyBandUpdateRequest.java | 83 + .../isp/GroupAffiliationQueryResponse.java | 92 + .../standard/isp/GroupAffiliationRequest.java | 91 + .../standard/isp/GroupDataServiceRequest.java | 93 + .../isp/GroupVoiceServiceRequest.java | 93 + .../isp/IndividualDataServiceRequest.java | 94 + .../isp/LocationRegistrationRequest.java | 115 + .../standard/isp/MessageUpdateRequest.java | 83 + .../isp/ProtectionParameterRequest.java | 91 + .../standard/isp/RadioUnitMonitorRequest.java | 88 + .../standard/isp/RoamingAddressRequest.java | 77 + .../standard/isp/RoamingAddressResponse.java | 107 + .../standard/isp/SNDCPDataChannelRequest.java | 102 + .../standard/isp/SNDCPDataPageResponse.java | 110 + .../standard/isp/SNDCPReconnectRequest.java | 109 + .../tsbk/standard/isp/StatusQueryRequest.java | 77 + .../standard/isp/StatusQueryResponse.java | 105 + .../standard/isp/StatusUpdateRequest.java | 105 + .../TelephoneInterconnectAnswerResponse.java | 90 + .../isp/TelephoneInterconnectPstnRequest.java | 95 + .../standard/isp/UnitAcknowledgeResponse.java | 90 + .../isp/UnitDeRegistrationRequest.java | 91 + .../standard/isp/UnitRegistrationRequest.java | 115 + .../UnitToUnitVoiceServiceAnswerResponse.java | 104 + .../isp/UnitToUnitVoiceServiceRequest.java | 94 + .../tsbk/standard/isp/UnknownISPMessage.java | 38 + .../standard/osp/AcknowledgeResponse.java | 180 + .../standard/osp/AdjacentStatusBroadcast.java | 210 + .../standard/osp/AuthenticationCommand.java | 89 + .../message/tsbk/standard/osp/CallAlert.java | 77 + .../tsbk/standard/osp/DenyResponse.java | 112 + .../standard/osp/ExtendedFunctionCommand.java | 88 + .../standard/osp/FrequencyBandUpdate.java | 144 + .../standard/osp/FrequencyBandUpdateTDMA.java | 157 + .../standard/osp/FrequencyBandUpdateVUHF.java | 155 + .../standard/osp/GroupAffiliationQuery.java | 77 + .../osp/GroupAffiliationResponse.java | 113 + .../osp/GroupDataChannelAnnouncement.java | 115 + .../GroupDataChannelAnnouncementExplicit.java | 90 + .../standard/osp/GroupDataChannelGrant.java | 137 + .../standard/osp/GroupVoiceChannelGrant.java | 117 + .../osp/GroupVoiceChannelGrantUpdate.java | 127 + .../GroupVoiceChannelGrantUpdateExplicit.java | 106 + .../osp/IndividualDataChannelGrant.java | 101 + .../osp/LocationRegistrationResponse.java | 140 + .../tsbk/standard/osp/MessageUpdate.java | 111 + .../standard/osp/NetworkStatusBroadcast.java | 148 + .../osp/ProtectionParameterUpdate.java | 99 + .../tsbk/standard/osp/QueuedResponse.java | 132 + .../standard/osp/RFSSStatusBroadcast.java | 175 + .../standard/osp/RadioUnitMonitorCommand.java | 108 + .../standard/osp/RoamingAddressCommand.java | 124 + .../SNDCPDataChannelAnnouncementExplicit.java | 151 + .../standard/osp/SNDCPDataChannelGrant.java | 148 + .../standard/osp/SNDCPDataPageRequest.java | 114 + .../osp/SecondaryControlChannelBroadcast.java | 174 + .../tsbk/standard/osp/StatusQuery.java | 97 + .../tsbk/standard/osp/StatusUpdate.java | 125 + .../tsbk/standard/osp/SyncBroadcast.java | 272 + .../standard/osp/SystemServiceBroadcast.java | 90 + .../TelephoneInterconnectAnswerRequest.java | 97 + ...elephoneInterconnectVoiceChannelGrant.java | 112 + ...neInterconnectVoiceChannelGrantUpdate.java | 112 + .../osp/UnitDeRegistrationAcknowledge.java | 111 + .../standard/osp/UnitRegistrationCommand.java | 97 + .../osp/UnitRegistrationResponse.java | 126 + .../standard/osp/UnitToUnitAnswerRequest.java | 94 + .../osp/UnitToUnitVoiceChannelGrant.java | 101 + .../UnitToUnitVoiceChannelGrantUpdate.java | 101 + .../tsbk/standard/osp/UnknownOSPMessage.java | 38 + .../unknown/isp/UnknownVendorISPMessage.java | 59 + .../unknown/osp/UnknownVendorOSPMessage.java | 59 + .../p25/message/tsbk/vendor/VendorOpcode.java | 111 - .../p25/message/vselp/VSELP1Message.java | 59 +- .../p25/message/vselp/VSELP2Message.java | 79 +- .../P25NetworkConfigurationMonitor.java | 487 ++ .../decode/p25/reference/AccessType.java | 31 + .../decode/p25/reference/AnswerResponse.java | 24 + .../decode/p25/reference/CancelReason.java | 46 + .../decode/p25/reference/Capability.java | 123 + .../decode/p25/reference/ChannelType.java | 105 + .../p25/reference/DataServiceOptions.java | 59 + .../p25/reference/DataSubscriberUnitType.java | 56 + .../decode/p25/reference/DataUnitID.java | 219 +- .../module/decode/p25/reference/Digit.java | 4 +- .../decode/p25/reference/Direction.java | 59 + .../module/decode/p25/reference/Duplex.java | 21 + .../p25/reference/ExtendedFunction.java | 134 +- .../p25/reference/IPHeaderCompression.java | 22 +- .../p25/reference/LinkControlOpcode.java | 106 - .../p25/reference/MaximumTransmitUnit.java | 59 + .../p25/reference/NetworkAddressType.java | 74 + .../module/decode/p25/reference/Opcode.java | 106 - .../decode/p25/reference/PDUFormat.java | 129 +- .../p25/reference/PDUPriorityMaximumType.java | 65 + .../module/decode/p25/reference/PDUType.java | 128 +- .../decode/p25/reference/PacketResponse.java | 51 + .../decode/p25/reference/ReadyTimer.java | 64 + ...ionRejectReason.java => RejectReason.java} | 6 +- .../p25/reference/ServiceAccessPoint.java | 206 +- .../decode/p25/reference/ServiceOptions.java | 99 + .../decode/p25/reference/SessionMode.java | 21 + .../decode/p25/reference/StandbyTimer.java | 64 + .../module/decode/p25/reference/Status.java | 37 +- .../p25/reference/UDPHeaderCompression.java | 56 + .../module/decode/p25/reference/Vocoder.java | 30 + .../p25/reference/VoiceServiceOptions.java | 64 + .../decode/passport/PassportCallEvent.java | 164 - .../decode/passport/PassportDecoder.java | 31 +- .../decode/passport/PassportDecoderState.java | 428 +- .../decode/passport/PassportMessage.java | 772 +-- .../passport/PassportMessageFilter.java | 83 +- .../passport/PassportMessageProcessor.java | 193 +- .../identifier/PassportTalkgroup.java | 67 + .../decode/tait/Tait1200ANIMessage.java | 348 +- .../tait/Tait1200ANIMessageProcessor.java | 96 +- .../module/decode/tait/Tait1200CallEvent.java | 101 +- .../module/decode/tait/Tait1200Decoder.java | 48 +- .../tait/Tait1200DecoderInstrumented.java | 28 +- .../decode/tait/Tait1200DecoderState.java | 151 +- .../decode/tait/Tait1200GPSMessage.java | 594 +- .../tait/Tait1200GPSMessageProcessor.java | 92 +- .../tait/identifier/TaitIdentifier.java | 63 + .../dsheirer/module/log/CallEventLogger.java | 62 - .../module/log/DecodeEventLogger.java | 136 + .../log/EventLogConfigurationEditor.java | 259 +- .../dsheirer/module/log/EventLogManager.java | 111 +- .../dsheirer/module/log/EventLogType.java | 10 +- .../dsheirer/module/log/EventLogger.java | 18 +- .../module/log/MessageEventLogger.java | 101 +- .../dsheirer/playlist/PlaylistManager.java | 201 +- .../dsheirer/playlist/PlaylistUpdater.java | 452 ++ .../github/dsheirer/playlist/PlaylistV2.java | 18 + .../dsheirer/preference/Preference.java | 52 + .../dsheirer/preference/PreferenceType.java | 31 + .../dsheirer/preference/TimestampFormat.java | 81 + .../dsheirer/preference/UserPreferences.java | 91 + .../event/DecodeEventPreference.java | 90 + .../preference/identifier/IntegerFormat.java | 43 + .../identifier/TalkgroupFormatPreference.java | 335 + .../talkgroup/APCO25TalkgroupFormatter.java | 98 + .../FleetsyncTalkgroupFormatter.java | 80 + .../talkgroup/IntegerFormatter.java | 47 + .../talkgroup/LtrNetTalkgroupFormatter.java | 80 + .../talkgroup/MPT1327TalkgroupFormatter.java | 80 + .../preference/playlist/FilePreferences.java | 279 + .../io/github/dsheirer/protocol/Protocol.java | 72 + .../github/dsheirer/record/AudioRecorder.java | 16 +- .../record/RecordConfigurationEditor.java | 24 - .../dsheirer/record/RecorderManager.java | 152 +- .../github/dsheirer/record/RecorderType.java | 4 +- .../dsheirer/record/binary/BinaryReader.java | 215 + .../record/binary/BinaryRecorder.java | 36 +- .../record/wave/AudioPacketWaveRecorder.java | 86 +- .../dsheirer/record/wave/WaveMetadata.java | 262 +- .../dsheirer/record/wave/WaveWriter.java | 45 +- .../sample/buffer/AbstractReusableBuffer.java | 1 + .../sample/buffer/ReusableAudioPacket.java | 141 +- .../ReusableComplexBufferAssembler.java | 27 +- .../tuner/airspy/AirspyTunerEditor.java | 46 +- .../tuner/usb/USBTransferProcessor.java | 136 +- .../spectrum/OverflowableBufferStream.java | 1 - .../dsheirer/spectrum/OverlayPanel.java | 53 +- .../spectrum/SpectralDisplayPanel.java | 2 +- .../dsheirer/spectrum/SpectrumPanel.java | 2 +- .../io/github/dsheirer/util/TimeStamp.java | 208 +- .../swingx/mapviewer/GeoPosition.java | 119 +- 869 files changed, 64776 insertions(+), 45233 deletions(-) delete mode 100644 src/main/java/io/github/dsheirer/alias/PatchGroupAlias.java rename src/main/java/io/github/dsheirer/alias/id/{ => legacy}/fleetsync/FleetsyncID.java (53%) rename src/main/java/io/github/dsheirer/alias/id/{ => legacy}/fleetsync/FleetsyncIDEditor.java (69%) rename src/main/java/io/github/dsheirer/alias/id/{ => legacy}/mdc/MDC1200ID.java (56%) create mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java rename src/main/java/io/github/dsheirer/alias/id/{ => legacy}/mpt1327/MPT1327ID.java (55%) rename src/main/java/io/github/dsheirer/alias/id/{ => legacy}/mpt1327/MPT1327IDEditor.java (68%) create mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java rename src/main/java/io/github/dsheirer/alias/id/{talkgroup/TalkgroupID.java => legacy/talkgroup/LegacyTalkgroupID.java} (50%) create mode 100644 src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200IDEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordable.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordableEditor.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/record/Record.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java delete mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupIDEditor.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java create mode 100644 src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java create mode 100644 src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java create mode 100644 src/main/java/io/github/dsheirer/audio/AudioMetadataProcessor.java create mode 100644 src/main/java/io/github/dsheirer/audio/AudioPacketManager.java delete mode 100644 src/main/java/io/github/dsheirer/audio/InversionFrequency.java create mode 100644 src/main/java/io/github/dsheirer/audio/invert/InversionFrequency.java rename src/main/java/io/github/dsheirer/audio/{ => playback}/AudioChannelPanel.java (53%) rename src/main/java/io/github/dsheirer/audio/{ => playback}/AudioChannelsPanel.java (58%) rename src/main/java/io/github/dsheirer/audio/{output => playback}/AudioOutput.java (92%) rename src/main/java/io/github/dsheirer/audio/{ => playback}/AudioPanel.java (92%) rename src/main/java/io/github/dsheirer/audio/{AudioManager.java => playback/AudioPlaybackManager.java} (89%) rename src/main/java/io/github/dsheirer/audio/{output => playback}/MonoAudioOutput.java (98%) rename src/main/java/io/github/dsheirer/audio/{output => playback}/StereoAudioOutput.java (98%) create mode 100644 src/main/java/io/github/dsheirer/channel/IChannelDescriptor.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/AliasedIdentifier.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/AliasedIntegerAttributeMonitor.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/AliasedStringAttributeMonitor.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/AttributeMonitor.java create mode 100644 src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java create mode 100644 src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataField.java create mode 100644 src/main/java/io/github/dsheirer/channel/metadata/IChannelMetadataUpdateListener.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/Metadata.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/MutableMetadata.java delete mode 100644 src/main/java/io/github/dsheirer/channel/metadata/MutableMetadataChangeEvent.java delete mode 100644 src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelAllocationEvent.java delete mode 100644 src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java create mode 100644 src/main/java/io/github/dsheirer/controller/channel/ChannelGrantEvent.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/IChannelEventBroadcaster.java delete mode 100644 src/main/java/io/github/dsheirer/controller/channel/TrafficChannelEvent.java create mode 100644 src/main/java/io/github/dsheirer/dsp/symbol/BinaryToByteBufferAssembler.java rename src/main/java/io/github/dsheirer/edac/trellis/{DMRNode.java => DMR_3_4_Node.java} (96%) create mode 100644 src/main/java/io/github/dsheirer/edac/trellis/P25_1_2_Node.java create mode 100644 src/main/java/io/github/dsheirer/edac/trellis/P25_3_4_Node.java create mode 100644 src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_1_2_P25.java create mode 100644 src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_P25.java create mode 100644 src/main/java/io/github/dsheirer/eventbus/MyEventBus.java create mode 100644 src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java create mode 100644 src/main/java/io/github/dsheirer/gui/preference/DecodeEventViewPreferenceEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java create mode 100644 src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java create mode 100644 src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorViewRequest.java create mode 100644 src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java create mode 100644 src/main/java/io/github/dsheirer/gui/preference/TalkgroupFormatPreferenceEditor.java create mode 100644 src/main/java/io/github/dsheirer/identifier/Form.java create mode 100644 src/main/java/io/github/dsheirer/identifier/Identifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/IdentifierClass.java create mode 100644 src/main/java/io/github/dsheirer/identifier/IdentifierCollection.java create mode 100644 src/main/java/io/github/dsheirer/identifier/IdentifierUpdateListener.java create mode 100644 src/main/java/io/github/dsheirer/identifier/IdentifierUpdateNotification.java create mode 100644 src/main/java/io/github/dsheirer/identifier/IdentifierUpdateProvider.java create mode 100644 src/main/java/io/github/dsheirer/identifier/MutableIdentifierCollection.java create mode 100644 src/main/java/io/github/dsheirer/identifier/Role.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/AliasListConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/ChannelDescriptorConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/ChannelNameConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationLongIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationStringIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/DecoderTypeConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/FrequencyConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/SiteConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/configuration/SystemConfigurationIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/decoder/DecoderLogicalChannelNameIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/decoder/DecoderStateIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKey.java create mode 100644 src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKeyIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/esn/ESNIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/integer/IntegerIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Address.java create mode 100644 src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Identifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/longnumber/LongIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/patch/PatchGroup.java create mode 100644 src/main/java/io/github/dsheirer/identifier/patch/PatchGroupIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/patch/PatchGroupManager.java create mode 100644 src/main/java/io/github/dsheirer/identifier/string/StringIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/identifier/talkgroup/LTRTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/identifier/talkgroup/TalkgroupIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/map/IPlottableUpdateListener.java delete mode 100644 src/main/java/io/github/dsheirer/map/Plottable.java delete mode 100644 src/main/java/io/github/dsheirer/map/PlottableEntity.java create mode 100644 src/main/java/io/github/dsheirer/map/PlottableEntityHistory.java delete mode 100644 src/main/java/io/github/dsheirer/map/PlottableUpdateListener.java create mode 100644 src/main/java/io/github/dsheirer/message/IBitErrorProvider.java create mode 100644 src/main/java/io/github/dsheirer/message/IMessage.java create mode 100644 src/main/java/io/github/dsheirer/message/SyncLossMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/event/CallEventAliasCellRenderer.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/event/CallEventModel.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/event/CallEventPanel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/event/ICallEventListener.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/event/ICallEventProvider.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventListener.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventProvider.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/MessageItem.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1Message.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1MessageProcessor.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncCallEvent.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncStatus.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/AutomaticNumberIdentification.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Fleetsync2Message.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/LocationReport.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Status.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/Header.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/IHeader.java rename src/main/java/io/github/dsheirer/module/decode/{p25/reference => ip}/IPProtocol.java (80%) create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/IPacket.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/Packet.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/PacketMessageFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/UnknownCompressedHeaderPacket.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/UnknownPacket.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeaderFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPDUType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPacket.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceDeRegistration.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistration.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistrationAcknowledge.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/QueryMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/RegistrationAcknowledgement.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistration.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistrationAcknowledge.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistration.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistrationAcknowledge.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Header.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Packet.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPPacket.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCodeIdentifier.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRCallEvent.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetISWMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetOSWMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/LtrNetMessageType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/channel/LtrNetChannel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/NeighborIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/SiteIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/UniqueIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessageFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallEnd.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallStart.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUniqueId.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUnknown.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnHigh.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnLow.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RequestAccess.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/Frequency.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyHigh.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyLow.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/LtrNetOswMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/LtrStandardMessageType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/channel/LtrChannel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Call.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEnd.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEndMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Idle.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/IdleMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCCallEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mdc1200/identifier/MDC1200Identifier.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327CallEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327IdentType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/channel/MPT1327Channel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/IdentType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327SiteIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327Talkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/IDataUnitDetectListener.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25CallEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelStatusProcessor.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25DataUnitDetector.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer2.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25Metadata.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25SyncDetector.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25Utils.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/PatchGroup.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/PatchGroupManager.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/Trellis_1_2_Rate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/Trellis_3_4_Rate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Lra.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Nac.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Rfss.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Site.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25System.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Wacn.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25Channel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25ExplicitChannel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25ExplicitChannel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/encryption/APCO25EncryptionKey.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/APCO25IpAddress.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/message/APCO25ShortDataMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/patch/APCO25PatchGroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/status/APCO25Status.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnnouncementTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnyTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25FromTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25IdentifierTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25Talkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25ToTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/identifier/telephone/APCO25TelephoneNumber.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/IBandIdentifier.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBandReceiver.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/IdentifierReceiver.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/P25MessageFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/UnknownP25Message.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/filter/AMBTCMessageFilter.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/filter/IPPacketMessageFilter.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SNDCPMessageFilter.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SyncLossMessageFilter.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/filter/UMBTCMessageFilter.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HeaderData.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlOpcode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWord.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWordFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/UnknownLinkControlWord.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupAdd.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupDelete.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUser.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaTalkComplete.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaUnknownOpcode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/MotorolaLinkControlWord.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcastExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallAlert.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallTermination.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCExtendedFunctionCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdateExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupAffiliationQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdateExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUser.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCMessageUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcastExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCProtectionParameterBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcastExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcastExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSystemServiceBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectAnswerRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectVoiceChannelUser.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitAuthenticationCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitRegistrationCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitAnswerRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitVoiceChannelUser.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/EncryptionSyncParameters.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallAlert.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallTermination.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/MessageUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusQuery.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeaderFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequence.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequenceMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCCallAlertRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCGroupAffiliationRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCIndividualDataServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCLocationRegistrationRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCMessageUpdateRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCRoamingAddressRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusUpdateRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitAcknowledgeResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceAnswerResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCAdjacentStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCCallAlert.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupDataChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupVoiceChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCIndividualDataChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCMessageUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCProtectionParameterBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitRegistrationResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitAnswerRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/ConfirmedDataBlock.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/DataBlock.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/UnconfirmedDataBlock.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PacketData.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextAccept.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextReject.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/DeActivateTdsContextRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessageFactory.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCHeader.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/isp/UMBTCTelephoneInterconnectRequestExplicitDialing.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDULinkControlMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallAlert.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallTermination.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/MessageUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusQuery.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ISPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/OSPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ServiceMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/UnitChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroup.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/SystemLoading.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/isp/UnknownMotorolaISPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelCWID.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelLoading.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupAdd.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupDelete.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PlannedChannelShutdown.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/UnknownMotorolaOSPMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/CallAlert.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/DenyResponse.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusQuery.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemService.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPData.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/AuthenticationQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CallAlertRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CancelServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/EmergencyAlarmRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ExtendedFunctionResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/FrequencyBandUpdateRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationQueryResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupDataServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupVoiceServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/IndividualDataServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/LocationRegistrationRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/MessageUpdateRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ProtectionParameterRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RadioUnitMonitorRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataChannelRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataPageResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPReconnectRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusUpdateRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectAnswerResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectPstnRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitAcknowledgeResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitDeRegistrationRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitRegistrationRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceAnswerResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnknownISPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AcknowledgeResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AdjacentStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AuthenticationCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/CallAlert.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/DenyResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ExtendedFunctionCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateTDMA.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateVUHF.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncement.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncementExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdateExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/IndividualDataChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/LocationRegistrationResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/MessageUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/NetworkStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ProtectionParameterUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/QueuedResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RFSSStatusBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RadioUnitMonitorCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RoamingAddressCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelAnnouncementExplicit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataPageRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SecondaryControlChannelBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusQuery.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SyncBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SystemServiceBroadcast.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectAnswerRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitDeRegistrationAcknowledge.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationCommand.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitAnswerRequest.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrant.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrantUpdate.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnknownOSPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/isp/UnknownVendorISPMessage.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/osp/UnknownVendorOSPMessage.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/vendor/VendorOpcode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/network/P25NetworkConfigurationMonitor.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/AccessType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/AnswerResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/CancelReason.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/Capability.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/ChannelType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/DataServiceOptions.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/DataSubscriberUnitType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/Direction.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/Duplex.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/LinkControlOpcode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/MaximumTransmitUnit.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/NetworkAddressType.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/Opcode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUPriorityMaximumType.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/PacketResponse.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/ReadyTimer.java rename src/main/java/io/github/dsheirer/module/decode/p25/reference/{SNDCPActivationRejectReason.java => RejectReason.java} (85%) create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceOptions.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/SessionMode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/StandbyTimer.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/UDPHeaderCompression.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/Vocoder.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/reference/VoiceServiceOptions.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/passport/PassportCallEvent.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/passport/identifier/PassportTalkgroup.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/tait/identifier/TaitIdentifier.java delete mode 100644 src/main/java/io/github/dsheirer/module/log/CallEventLogger.java create mode 100644 src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java create mode 100644 src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java create mode 100644 src/main/java/io/github/dsheirer/preference/Preference.java create mode 100644 src/main/java/io/github/dsheirer/preference/PreferenceType.java create mode 100644 src/main/java/io/github/dsheirer/preference/TimestampFormat.java create mode 100644 src/main/java/io/github/dsheirer/preference/UserPreferences.java create mode 100644 src/main/java/io/github/dsheirer/preference/event/DecodeEventPreference.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/IntegerFormat.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/talkgroup/APCO25TalkgroupFormatter.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/talkgroup/FleetsyncTalkgroupFormatter.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/talkgroup/IntegerFormatter.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/talkgroup/LtrNetTalkgroupFormatter.java create mode 100644 src/main/java/io/github/dsheirer/preference/identifier/talkgroup/MPT1327TalkgroupFormatter.java create mode 100644 src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java create mode 100644 src/main/java/io/github/dsheirer/protocol/Protocol.java create mode 100644 src/main/java/io/github/dsheirer/record/binary/BinaryReader.java diff --git a/build.gradle b/build.gradle index 25c9fa40..db9cbf67 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,7 @@ dependencies { compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.3' compile 'org.kordamp.ikonli:ikonli-javafx:2.1.1' compile 'org.kordamp.ikonli:ikonli-fontawesome-pack:2.1.1' + compile 'com.google.guava:guava:27.0-jre' } task wrapper(type: Wrapper) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 34c937c9..ec178c54 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-bin.zip +#Sun Nov 25 05:28:28 EST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip diff --git a/sdr-trunk.ipr b/sdr-trunk.ipr index 912b20d7..28efe348 100644 --- a/sdr-trunk.ipr +++ b/sdr-trunk.ipr @@ -1,79 +1,728 @@ - - - + + + + + - + + - - + - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + baseband + costas + + - + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -83,7 +732,7 @@ @@ -94,11 +743,11 @@ - + - + - + \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/alias/Alias.java b/src/main/java/io/github/dsheirer/alias/Alias.java index 588995cd..cd50e0db 100644 --- a/src/main/java/io/github/dsheirer/alias/Alias.java +++ b/src/main/java/io/github/dsheirer/alias/Alias.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.alias; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -24,10 +26,10 @@ import io.github.dsheirer.alias.action.AliasAction; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.alias.id.AliasIDType; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; -import io.github.dsheirer.alias.id.nonrecordable.NonRecordable; import io.github.dsheirer.alias.id.priority.Priority; +import io.github.dsheirer.alias.id.record.Record; -import java.awt.*; +import java.awt.Color; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -187,7 +189,7 @@ public class Alias * Returns the priority level of this alias, if defined, or the default priority */ @JsonIgnore - public int getCallPriority() + public int getPlaybackPriority() { for(AliasID id : mAliasIDs) { @@ -245,27 +247,39 @@ public class Alias { for(AliasID id : getId()) { - if(id.getType() == AliasIDType.NON_RECORDABLE) + if(id.getType() == AliasIDType.RECORD) { - return false; + return true; } } - return true; + return false; } /** - * Sets or removes the non-recordable alias ID for this alias. + * Sets or removes the record alias ID for this alias. */ public void setRecordable(boolean recordable) { if(recordable) + { + for(AliasID id : getId()) + { + if(id.getType() == AliasIDType.RECORD) + { + return; + } + } + + addAliasID(new Record()); + } + else { AliasID toRemove = null; for(AliasID id : getId()) { - if(id.getType() == AliasIDType.NON_RECORDABLE) + if(id.getType() == AliasIDType.RECORD) { toRemove = id; break; @@ -277,18 +291,6 @@ public class Alias removeAliasID(toRemove); } } - else - { - for(AliasID id : getId()) - { - if(id.getType() == AliasIDType.NON_RECORDABLE) - { - return; - } - } - - addAliasID(new NonRecordable()); - } } /** diff --git a/src/main/java/io/github/dsheirer/alias/AliasFactory.java b/src/main/java/io/github/dsheirer/alias/AliasFactory.java index ad28d053..102007cb 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasFactory.java +++ b/src/main/java/io/github/dsheirer/alias/AliasFactory.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.alias; import io.github.dsheirer.alias.action.AliasAction; @@ -31,26 +33,32 @@ import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; import io.github.dsheirer.alias.id.broadcast.BroadcastChannelEditor; import io.github.dsheirer.alias.id.esn.ESNEditor; import io.github.dsheirer.alias.id.esn.Esn; -import io.github.dsheirer.alias.id.fleetsync.FleetsyncID; -import io.github.dsheirer.alias.id.fleetsync.FleetsyncIDEditor; +import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID; +import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncIDEditor; +import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID; +import io.github.dsheirer.alias.id.legacy.mdc.MDC1200IDEditor; +import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID; +import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327IDEditor; +import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable; +import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordableEditor; +import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID; +import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupIDEditor; import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID; import io.github.dsheirer.alias.id.lojack.LoJackIDEditor; -import io.github.dsheirer.alias.id.mdc.MDC1200ID; -import io.github.dsheirer.alias.id.mdc.MDC1200IDEditor; import io.github.dsheirer.alias.id.mobileID.MINEditor; import io.github.dsheirer.alias.id.mobileID.Min; -import io.github.dsheirer.alias.id.mpt1327.MPT1327ID; -import io.github.dsheirer.alias.id.mpt1327.MPT1327IDEditor; -import io.github.dsheirer.alias.id.nonrecordable.NonRecordable; -import io.github.dsheirer.alias.id.nonrecordable.NonRecordableEditor; import io.github.dsheirer.alias.id.priority.Priority; import io.github.dsheirer.alias.id.priority.PriorityEditor; +import io.github.dsheirer.alias.id.record.Record; +import io.github.dsheirer.alias.id.record.RecordEditor; import io.github.dsheirer.alias.id.siteID.SiteID; import io.github.dsheirer.alias.id.siteID.SiteIDEditor; import io.github.dsheirer.alias.id.status.StatusID; import io.github.dsheirer.alias.id.status.StatusIDEditor; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupID; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupIDEditor; +import io.github.dsheirer.alias.id.talkgroup.Talkgroup; +import io.github.dsheirer.alias.id.talkgroup.TalkgroupEditor; +import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange; +import io.github.dsheirer.alias.id.talkgroup.TalkgroupRangeEditor; import io.github.dsheirer.alias.id.uniqueID.UniqueID; import io.github.dsheirer.alias.id.uniqueID.UniqueIDEditor; import io.github.dsheirer.audio.broadcast.BroadcastModel; @@ -59,246 +67,255 @@ import io.github.dsheirer.gui.editor.EmptyEditor; public class AliasFactory { - public static AliasID copyOf( AliasID id ) - { - switch( id.getType() ) - { - case BROADCAST_CHANNEL: - BroadcastChannel originalBroadcast = (BroadcastChannel)id; - BroadcastChannel copyBroadcast = new BroadcastChannel(); - copyBroadcast.setChannelName(originalBroadcast.getChannelName()); - return copyBroadcast; - case ESN: - Esn originalESN = (Esn)id; - Esn copyESN = new Esn(); - copyESN.setEsn( originalESN.getEsn() ); - return copyESN; - case FLEETSYNC: - FleetsyncID originalFleetsyncID = (FleetsyncID)id; - FleetsyncID copyFleetsyncID = new FleetsyncID(); - copyFleetsyncID.setIdent( originalFleetsyncID.getIdent() ); - return copyFleetsyncID; - case LTR_NET_UID: - UniqueID originalUniqueID = (UniqueID)id; - UniqueID copyUniqueID = new UniqueID(); - copyUniqueID.setUid( originalUniqueID.getUid() ); - return copyUniqueID; - case LOJACK: - LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id; - LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID(); - copyLoJackFunctionAndID.setFunction( originalLoJackFunctionAndID.getFunction() ); - copyLoJackFunctionAndID.setID( originalLoJackFunctionAndID.getID() ); - return copyLoJackFunctionAndID; - case MDC1200: - MDC1200ID originalMDC1200ID = (MDC1200ID)id; - MDC1200ID copyMDC1200ID = new MDC1200ID(); - copyMDC1200ID.setIdent( originalMDC1200ID.getIdent() ); - return copyMDC1200ID; - case MIN: - Min originalMin = (Min)id; - Min copyMin = new Min(); - copyMin.setMin( originalMin.getMin() ); - return copyMin; - case MPT1327: - MPT1327ID originalMPT1327ID = (MPT1327ID)id; - MPT1327ID copyMPT1327ID = new MPT1327ID(); - copyMPT1327ID.setIdent( originalMPT1327ID.getIdent() ); - return copyMPT1327ID; - case NON_RECORDABLE: - return new NonRecordable(); - case PRIORITY: - Priority originalPriority = (Priority)id; - Priority copyPriority = new Priority(); - copyPriority.setPriority( originalPriority.getPriority() ); - return copyPriority; - case SITE: - SiteID originalSiteID = (SiteID)id; - SiteID copySiteID = new SiteID(); - copySiteID.setSite( originalSiteID.getSite() ); - return copySiteID; - case TALKGROUP: - TalkgroupID originalTalkgroupID = (TalkgroupID)id; - TalkgroupID copyTalkgroupID = new TalkgroupID(); - copyTalkgroupID.setTalkgroup( originalTalkgroupID.getTalkgroup() ); - return copyTalkgroupID; - case STATUS: - StatusID originalStatusID = (StatusID)id; - StatusID copyStatusID = new StatusID(); - copyStatusID.setStatus( originalStatusID.getStatus() ); - return copyStatusID; - default: - } + public static AliasID copyOf(AliasID id) + { + switch(id.getType()) + { + case BROADCAST_CHANNEL: + BroadcastChannel originalBroadcast = (BroadcastChannel)id; + BroadcastChannel copyBroadcast = new BroadcastChannel(); + copyBroadcast.setChannelName(originalBroadcast.getChannelName()); + return copyBroadcast; + case ESN: + Esn originalESN = (Esn)id; + Esn copyESN = new Esn(); + copyESN.setEsn(originalESN.getEsn()); + return copyESN; + case LTR_NET_UID: + UniqueID originalUniqueID = (UniqueID)id; + UniqueID copyUniqueID = new UniqueID(); + copyUniqueID.setUid(originalUniqueID.getUid()); + return copyUniqueID; + case LOJACK: + LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id; + LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID(); + copyLoJackFunctionAndID.setFunction(originalLoJackFunctionAndID.getFunction()); + copyLoJackFunctionAndID.setID(originalLoJackFunctionAndID.getID()); + return copyLoJackFunctionAndID; + case MIN: + Min originalMin = (Min)id; + Min copyMin = new Min(); + copyMin.setMin(originalMin.getMin()); + return copyMin; + case PRIORITY: + Priority originalPriority = (Priority)id; + Priority copyPriority = new Priority(); + copyPriority.setPriority(originalPriority.getPriority()); + return copyPriority; + case RECORD: + return new Record(); + case SITE: + SiteID originalSiteID = (SiteID)id; + SiteID copySiteID = new SiteID(); + copySiteID.setSite(originalSiteID.getSite()); + return copySiteID; + case STATUS: + StatusID originalStatusID = (StatusID)id; + StatusID copyStatusID = new StatusID(); + copyStatusID.setStatus(originalStatusID.getStatus()); + return copyStatusID; + case TALKGROUP: + Talkgroup originalTalkgroup = (Talkgroup)id; + Talkgroup copyTalkgroup = new Talkgroup(originalTalkgroup.getProtocol(), originalTalkgroup.getValue()); + return copyTalkgroup; + case TALKGROUP_RANGE: + TalkgroupRange originalRange = (TalkgroupRange)id; + TalkgroupRange copyRange = new TalkgroupRange(originalRange.getProtocol(), originalRange.getMinTalkgroup(), + originalRange.getMaxTalkgroup()); + return copyRange; - return null; - } - - public static AliasAction copyOf( AliasAction action ) - { - if( action instanceof BeepAction ) - { - return new BeepAction(); - } - else if( action instanceof ClipAction) - { - ClipAction originalClip = (ClipAction)action; - ClipAction copyClip = new ClipAction(); - copyClip.setInterval( originalClip.getInterval() ); - copyClip.setPath( originalClip.getPath() ); - copyClip.setPeriod( originalClip.getPeriod() ); - return copyClip; - } - else if( action instanceof ScriptAction ) - { - ScriptAction originalScript = (ScriptAction)action; - ScriptAction copyScript = new ScriptAction(); - copyScript.setInterval( originalScript.getInterval() ); - copyScript.setPeriod( originalScript.getPeriod() ); - copyScript.setScript( originalScript.getScript() ); - return copyScript; - } - - return null; - } - - public static Alias copyOf( Alias original ) - { - Alias copy = new Alias( original.getName() ); - copy.setList( original.getList() ); - copy.setGroup( original.getGroup() ); - copy.setColor( original.getColor() ); - copy.setIconName( original.getIconName() ); - - for( AliasID id: original.getId() ) - { - AliasID copyID = copyOf( id ); - - if( copyID != null ) - { - copy.addAliasID( copyID ); - } - } + //Legacy identifiers ... not supported + case FLEETSYNC: + case LEGACY_TALKGROUP: + case MDC1200: + case MPT1327: + case NON_RECORDABLE: + default: + } - for( AliasAction action: original.getAction() ) - { - AliasAction copyAction = copyOf( action ); - - if( copyAction != null ) - { - copy.addAliasAction( copyAction ); - } - } - - return copy; - } + return null; + } + + public static AliasAction copyOf(AliasAction action) + { + if(action instanceof BeepAction) + { + return new BeepAction(); + } + else if(action instanceof ClipAction) + { + ClipAction originalClip = (ClipAction)action; + ClipAction copyClip = new ClipAction(); + copyClip.setInterval(originalClip.getInterval()); + copyClip.setPath(originalClip.getPath()); + copyClip.setPeriod(originalClip.getPeriod()); + return copyClip; + } + else if(action instanceof ScriptAction) + { + ScriptAction originalScript = (ScriptAction)action; + ScriptAction copyScript = new ScriptAction(); + copyScript.setInterval(originalScript.getInterval()); + copyScript.setPeriod(originalScript.getPeriod()); + copyScript.setScript(originalScript.getScript()); + return copyScript; + } + + return null; + } + + public static Alias copyOf(Alias original) + { + Alias copy = new Alias(original.getName()); + copy.setList(original.getList()); + copy.setGroup(original.getGroup()); + copy.setColor(original.getColor()); + copy.setIconName(original.getIconName()); + + for(AliasID id : original.getId()) + { + AliasID copyID = copyOf(id); + + if(copyID != null) + { + copy.addAliasID(copyID); + } + } + + for(AliasAction action : original.getAction()) + { + AliasAction copyAction = copyOf(action); + + if(copyAction != null) + { + copy.addAliasAction(copyAction); + } + } + + return copy; + } + + public static Editor getEditor(AliasID aliasID, BroadcastModel broadcastModel) + { + if(aliasID != null) + { + switch(aliasID.getType()) + { + case BROADCAST_CHANNEL: + return new BroadcastChannelEditor(aliasID, broadcastModel); + case ESN: + return new ESNEditor(aliasID); + case LTR_NET_UID: + return new UniqueIDEditor(aliasID); + case LOJACK: + return new LoJackIDEditor(aliasID); + case MIN: + return new MINEditor(aliasID); + case PRIORITY: + return new PriorityEditor(aliasID); + case RECORD: + return new RecordEditor(aliasID); + case SITE: + return new SiteIDEditor(aliasID); + case STATUS: + return new StatusIDEditor(aliasID); + case TALKGROUP: + return new TalkgroupEditor(aliasID); + case TALKGROUP_RANGE: + return new TalkgroupRangeEditor(aliasID); + + case FLEETSYNC: + return new FleetsyncIDEditor(aliasID); + case LEGACY_TALKGROUP: + return new LegacyTalkgroupIDEditor(aliasID); + case MDC1200: + return new MDC1200IDEditor(aliasID); + case MPT1327: + return new MPT1327IDEditor(aliasID); + case NON_RECORDABLE: + return new NonRecordableEditor(aliasID); + default: + break; + } + } + + return new EmptyEditor(); + } + + public static Editor getEditor(AliasAction aliasAction) + { + if(aliasAction != null) + { + switch(aliasAction.getType()) + { + case BEEP: + return new BeepActionEditor(aliasAction); + case CLIP: + return new ClipActionEditor(aliasAction); + case SCRIPT: + return new ScriptActionEditor(aliasAction); + default: + break; + } + } + + return new EmptyEditor(); + } + + public static AliasID getAliasID(AliasIDType type) + { + switch(type) + { + case BROADCAST_CHANNEL: + return new BroadcastChannel(); + case ESN: + return new Esn(); + case FLEETSYNC: + return new FleetsyncID(); + case LTR_NET_UID: + return new UniqueID(); + case LOJACK: + return new LoJackFunctionAndID(); + case MDC1200: + return new MDC1200ID(); + case MIN: + return new Min(); + case MPT1327: + return new MPT1327ID(); + case NON_RECORDABLE: + return new NonRecordable(); + case PRIORITY: + return new Priority(); + case RECORD: + return new Record(); + case TALKGROUP: + return new Talkgroup(); + case TALKGROUP_RANGE: + return new TalkgroupRange(); + case SITE: + return new SiteID(); + case STATUS: + return new StatusID(); + case LEGACY_TALKGROUP: + return new LegacyTalkgroupID(); + default: + throw new IllegalArgumentException("Unrecognized Alias ID type: " + type); + } + } + + public static AliasAction getAliasAction(AliasActionType type) + { + switch(type) + { + case BEEP: + return new BeepAction(); + case CLIP: + return new ClipAction(); + case SCRIPT: + return new ScriptAction(); + default: + throw new IllegalArgumentException("Unrecognized Alias Action type: " + type); + } + } - public static Editor getEditor( AliasID aliasID, BroadcastModel broadcastModel ) - { - if( aliasID != null ) - { - switch( aliasID.getType() ) - { - case BROADCAST_CHANNEL: - return new BroadcastChannelEditor(aliasID, broadcastModel); - case ESN: - return new ESNEditor( aliasID ); - case FLEETSYNC: - return new FleetsyncIDEditor( aliasID ); - case LTR_NET_UID: - return new UniqueIDEditor( aliasID ); - case LOJACK: - return new LoJackIDEditor( aliasID ); - case MDC1200: - return new MDC1200IDEditor( aliasID ); - case MIN: - return new MINEditor( aliasID ); - case MPT1327: - return new MPT1327IDEditor( aliasID ); - case NON_RECORDABLE: - return new NonRecordableEditor( aliasID ); - case PRIORITY: - return new PriorityEditor( aliasID ); - case SITE: - return new SiteIDEditor( aliasID ); - case STATUS: - return new StatusIDEditor( aliasID ); - case TALKGROUP: - return new TalkgroupIDEditor( aliasID ); - default: - break; - } - } - - return new EmptyEditor(); - } - - public static Editor getEditor( AliasAction aliasAction ) - { - if( aliasAction != null ) - { - switch( aliasAction.getType() ) - { - case BEEP: - return new BeepActionEditor( aliasAction ); - case CLIP: - return new ClipActionEditor( aliasAction ); - case SCRIPT: - return new ScriptActionEditor( aliasAction ); - default: - break; - } - } - - return new EmptyEditor(); - } - - public static AliasID getAliasID( AliasIDType type ) - { - switch( type ) - { - case BROADCAST_CHANNEL: - return new BroadcastChannel(); - case ESN: - return new Esn(); - case FLEETSYNC: - return new FleetsyncID(); - case LTR_NET_UID: - return new UniqueID(); - case LOJACK: - return new LoJackFunctionAndID(); - case MDC1200: - return new MDC1200ID(); - case MIN: - return new Min(); - case MPT1327: - return new MPT1327ID(); - case NON_RECORDABLE: - return new NonRecordable(); - case PRIORITY: - return new Priority(); - case SITE: - return new SiteID(); - case STATUS: - return new StatusID(); - case TALKGROUP: - return new TalkgroupID(); - default: - throw new IllegalArgumentException( "Unrecognized Alias ID type: " + type ); - } - } - - public static AliasAction getAliasAction( AliasActionType type ) - { - switch( type ) - { - case BEEP: - return new BeepAction(); - case CLIP: - return new ClipAction(); - case SCRIPT: - return new ScriptAction(); - default: - throw new IllegalArgumentException( "Unrecognized Alias Action type: " + type ); - } - } - } diff --git a/src/main/java/io/github/dsheirer/alias/AliasList.java b/src/main/java/io/github/dsheirer/alias/AliasList.java index b51503bf..a6051822 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasList.java +++ b/src/main/java/io/github/dsheirer/alias/AliasList.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,24 +15,25 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.alias; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.alias.id.WildcardID; +import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; import io.github.dsheirer.alias.id.esn.Esn; -import io.github.dsheirer.alias.id.fleetsync.FleetsyncID; -import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID; -import io.github.dsheirer.alias.id.mdc.MDC1200ID; -import io.github.dsheirer.alias.id.mobileID.Min; -import io.github.dsheirer.alias.id.mpt1327.MPT1327ID; +import io.github.dsheirer.alias.id.priority.Priority; import io.github.dsheirer.alias.id.siteID.SiteID; import io.github.dsheirer.alias.id.status.StatusID; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupID; +import io.github.dsheirer.alias.id.talkgroup.Talkgroup; import io.github.dsheirer.alias.id.uniqueID.UniqueID; -import io.github.dsheirer.module.decode.lj1200.LJ1200Message; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.esn.ESNIdentifier; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,31 +43,23 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; public class AliasList implements Listener { private final static Logger mLog = LoggerFactory.getLogger(AliasList.class); public static final String WILDCARD = "*"; - public static final String REGEX_WILDCARD = "."; private Map mESN = new HashMap<>(); - private Map mFleetsync = new HashMap<>(); - private Map mLoJack = new HashMap<>(); - private Map mMDC1200 = new HashMap<>(); - private Map mMobileID = new HashMap<>(); - private Map mMPT1327 = new HashMap<>(); private Map mSiteID = new HashMap<>(); private Map mStatus = new HashMap<>(); - private Map mTalkgroup = new HashMap<>(); private Map mUniqueID = new HashMap<>(); private List mESNWildcards = new ArrayList<>(); - private List mMobileIDWildcards = new ArrayList<>(); - private List mFleetsyncWildcards = new ArrayList<>(); - private List mMDC1200Wildcards = new ArrayList<>(); - private List mMPT1327Wildcards = new ArrayList<>(); private List mSiteWildcards = new ArrayList<>(); - private List mTalkgroupWildcards = new ArrayList<>(); + private boolean mHasAliasActions = false; + + private Map> mTalkgroupProtocolMap = new HashMap<>(); private String mName; @@ -92,6 +86,11 @@ public class AliasList implements Listener addAliasID(aliasID, alias); } } + + if(alias.hasActions()) + { + mHasAliasActions = true; + } } /** @@ -105,6 +104,19 @@ public class AliasList implements Listener { switch(id.getType()) { + case TALKGROUP: + Talkgroup talkgroup = (Talkgroup)id; + + Map protocolMap = mTalkgroupProtocolMap.get(talkgroup.getProtocol()); + + if(protocolMap == null) + { + protocolMap = new TreeMap(); + mTalkgroupProtocolMap.put(talkgroup.getProtocol(), protocolMap); + } + + protocolMap.put(talkgroup.getValue(), alias); + break; case ESN: String esn = ((Esn) id).getEsn(); @@ -119,65 +131,6 @@ public class AliasList implements Listener mESN.put(esn, alias); } break; - case FLEETSYNC: - String fleetsync = ((FleetsyncID) id).getIdent(); - - if(fleetsync != null) - { - if(fleetsync.contains(WILDCARD)) - { - mFleetsyncWildcards.add(new WildcardID(fleetsync)); - Collections.sort(mFleetsyncWildcards); - } - - mFleetsync.put(fleetsync, alias); - } - break; - case LOJACK: - mLoJack.put((LoJackFunctionAndID) id, alias); - break; - case MDC1200: - String mdc = ((MDC1200ID) id).getIdent(); - - if(mdc != null) - { - if(mdc.contains(WILDCARD)) - { - mMDC1200Wildcards.add(new WildcardID(mdc)); - Collections.sort(mMDC1200Wildcards); - } - - mMDC1200.put(mdc, alias); - } - break; - case MPT1327: - String mpt = ((MPT1327ID) id).getIdent(); - - if(mpt != null) - { - if(mpt.contains(WILDCARD)) - { - mMPT1327Wildcards.add(new WildcardID(mpt)); - Collections.sort(mMPT1327Wildcards); - } - - mMPT1327.put(mpt, alias); - } - break; - case MIN: - String min = ((Min) id).getMin(); - - if(min != null) - { - if(min.contains(WILDCARD)) - { - mMobileIDWildcards.add(new WildcardID(min)); - Collections.sort(mMobileIDWildcards); - } - - mMobileID.put(min, alias); - } - break; case LTR_NET_UID: mUniqueID.put(((UniqueID) id).getUid(), alias); break; @@ -198,20 +151,6 @@ public class AliasList implements Listener case STATUS: mStatus.put(((StatusID) id).getStatus(), alias); break; - case TALKGROUP: - String tgid = ((TalkgroupID) id).getTalkgroup(); - - if(tgid != null) - { - if(tgid.contains(WILDCARD)) - { - mTalkgroupWildcards.add(new WildcardID(tgid)); - Collections.sort(mTalkgroupWildcards); - } - - mTalkgroup.put(tgid, alias); - } - break; case BROADCAST_CHANNEL: case NON_RECORDABLE: case PRIORITY: @@ -279,61 +218,6 @@ public class AliasList implements Listener mESN.remove(esn); break; - case FLEETSYNC: - String fleetsync = ((FleetsyncID) id).getIdent(); - - if(fleetsync != null) - { - if(fleetsync.contains(WILDCARD)) - { - removeWildcard(fleetsync, mFleetsyncWildcards); - } - - mFleetsync.remove(fleetsync); - } - break; - case LOJACK: - mLoJack.remove((LoJackFunctionAndID) id); - break; - case MDC1200: - String mdc = ((MDC1200ID) id).getIdent(); - - if(mdc != null) - { - if(mdc.contains(WILDCARD)) - { - removeWildcard(mdc, mMDC1200Wildcards); - } - - mMDC1200.remove(mdc); - } - break; - case MPT1327: - String mpt = ((MPT1327ID) id).getIdent(); - - if(mpt != null) - { - if(mpt.contains(WILDCARD)) - { - removeWildcard(mpt, mMPT1327Wildcards); - } - - mMPT1327.remove(mpt); - } - break; - case MIN: - String min = ((Min) id).getMin(); - - if(min != null) - { - if(min.contains(WILDCARD)) - { - removeWildcard(min, mMobileIDWildcards); - } - - mMobileID.remove(min); - } - break; case LTR_NET_UID: mUniqueID.remove(((UniqueID) id).getUid()); break; @@ -343,19 +227,6 @@ public class AliasList implements Listener case STATUS: mStatus.remove(((StatusID) id).getStatus()); break; - case TALKGROUP: - String tgid = ((TalkgroupID) id).getTalkgroup(); - - if(tgid != null) - { - if(tgid.contains(WILDCARD)) - { - removeWildcard(tgid, mTalkgroupWildcards); - } - - mTalkgroup.remove(tgid); - } - break; case NON_RECORDABLE: case PRIORITY: case BROADCAST_CHANNEL: @@ -458,162 +329,6 @@ public class AliasList implements Listener return alias; } - /** - * Lookup alias by fleetsync ID - */ - public Alias getFleetsyncAlias(String ident) - { - Alias alias = null; - - if(ident != null) - { - alias = mFleetsync.get(ident); - - if(alias == null) - { - String wildcard = getWildcardMatch(ident, mFleetsyncWildcards); - - if(wildcard != null) - { - alias = mFleetsync.get(wildcard); - } - } - } - - return alias; - } - - /** - * Lookup alias by lojack function and ID - */ - public Alias getLoJackAlias(LJ1200Message.Function function, String id) - { - if(id != null) - { - for(LoJackFunctionAndID lojack : mLoJack.keySet()) - { - if(lojack.matches(function, id)) - { - return mLoJack.get(lojack); - } - } - } - - return null; - } - - /** - * Lookup alias by MDC1200 ID - */ - public Alias getMDC1200Alias(String ident) - { - Alias alias = null; - - if(ident != null) - { - alias = mMDC1200.get(ident); - - if(alias == null) - { - String wildcard = getWildcardMatch(ident, mMDC1200Wildcards); - - if(wildcard != null) - { - alias = mMDC1200.get(wildcard); - } - } - } - - return alias; - } - - /** - * Lookup alias by MPT1327 Ident - */ - public Alias getMPT1327Alias(String ident) - { - Alias alias = null; - - if(ident != null) - { - alias = mMPT1327.get(ident); - - if(alias == null) - { - String wildcard = getWildcardMatch(ident, mMPT1327Wildcards); - - if(wildcard != null) - { - alias = mMPT1327.get(wildcard); - } - } - } - - return alias; - } - - /** - * Lookup alias by Mobile ID Number (MIN) - */ - public Alias getMobileIDNumberAlias(String ident) - { - Alias alias = null; - - if(ident != null) - { - alias = mMobileID.get(ident); - - if(alias == null) - { - String wildcard = getWildcardMatch(ident, mMobileIDWildcards); - - if(wildcard != null) - { - alias = mMobileID.get(wildcard); - } - } - } - - return alias; - } - - /** - * Lookup alias by talkgroup - * - * @param tgid to use when searching for an alias - * @param includeWildcards true if you want to additionally search for a matching alias that contains wildcard(s) - * when there is not a direct match to the TGID. - */ - public Alias getTalkgroupAlias(String tgid, boolean includeWildcards) - { - Alias alias = null; - - if(tgid != null) - { - alias = mTalkgroup.get(tgid); - - if(alias == null && includeWildcards) - { - String wildcard = getWildcardMatch(tgid, mTalkgroupWildcards); - - if(wildcard != null) - { - alias = mTalkgroup.get(wildcard); - } - } - } - - return alias; - } - - /** - * Lookup alias by talkgroup. Defaults to matching to wildcard talkgroups. - */ - public Alias getTalkgroupAlias(String tgid) - { - return getTalkgroupAlias(tgid, true); - } - /** * Alias list name */ @@ -674,4 +389,136 @@ public class AliasList implements Listener } } } + + /** + * Returns an optional alias that is associated with the identifier + * @param identifier to alias + * @return alias or null + */ + public Alias getAlias(Identifier identifier) + { + if(identifier != null) + { + switch(identifier.getForm()) + { + case TALKGROUP: + TalkgroupIdentifier talkgroup = (TalkgroupIdentifier)identifier; + Map protocolMap = mTalkgroupProtocolMap.get(identifier.getProtocol()); + + if(protocolMap != null) + { + return protocolMap.get(talkgroup.getValue()); + } + break; + case ESN: + if(identifier instanceof ESNIdentifier) + { + return getESNAlias(((ESNIdentifier)identifier).getValue()); + } + break; + } + } + + return null; + } + + /** + * Indicates if any of the identifiers contain a broadcast channel for streaming of audio. + * @param identifierCollection to inspect + * @return true if the identifier collection is designated for streaming to one or more channels. + */ + public boolean isStreamable(IdentifierCollection identifierCollection) + { + for(Identifier identifier: identifierCollection.getIdentifiers()) + { + Alias alias = getAlias(identifier); + + if(alias != null && alias.isStreamable()) + { + return true; + } + } + + return false; + } + + /** + * Indicates if any of the identifiers have been identified for recording. + * @param identifierCollection to inspect + * @return true if recordable. + */ + public boolean isRecordable(IdentifierCollection identifierCollection) + { + for(Identifier identifier: identifierCollection.getIdentifiers()) + { + Alias alias = getAlias(identifier); + + if(alias != null && alias.isRecordable()) + { + return true; + } + } + + return false; + } + + /** + * Indicates if any of the aliases in this list have an associated alias action + */ + public boolean hasAliasActions() + { + return mHasAliasActions; + } + + /** + * Returns the lowest audio playback priority specified by aliases for identifiers in the + * identifier collection. + * + * @param identifierCollection to inspect for audio priority + * @return audio playback priority + */ + public int getAudioPlaybackPriority(IdentifierCollection identifierCollection) + { + int priority = Priority.DEFAULT_PRIORITY; + + for(Identifier identifier: identifierCollection.getIdentifiers()) + { + Alias alias = getAlias(identifier); + + if(alias != null && alias.getPlaybackPriority() < priority) + { + priority = alias.getPlaybackPriority(); + } + } + + return priority; + } + + /** + * Returns a list of streaming broadcast channels specified for any of the identifiers in the collection. + * + * @return list of broadcast channels or an empty list + */ + public List getBroadcastChannels(IdentifierCollection identifierCollection) + { + List channels = new ArrayList<>(); + + for(Identifier identifier: identifierCollection.getIdentifiers()) + { + Alias alias = getAlias(identifier); + + if(alias != null && alias.isStreamable()) + { + for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels()) + { + if(!channels.contains(broadcastChannel)) + { + channels.add(broadcastChannel); + } + } + } + } + + return channels; + } } diff --git a/src/main/java/io/github/dsheirer/alias/AliasModel.java b/src/main/java/io/github/dsheirer/alias/AliasModel.java index 2a62f05f..daba8830 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasModel.java +++ b/src/main/java/io/github/dsheirer/alias/AliasModel.java @@ -1,23 +1,27 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.alias; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; @@ -30,7 +34,9 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; /** - * Alias Model + * Alias Model contains all aliases and is responsible for creation and management of alias lists. Alias lists are a + * set of aliases that all share a common alias list name and can be attached to a decoding channel for aliasing + * identifiers produced by channel decoder(s). */ public class AliasModel extends AbstractTableModel { @@ -68,31 +74,69 @@ public class AliasModel extends AbstractTableModel return null; } + /** + * Returns an optional alias list associated with the identifier collection + * + * @return alias list or null + */ + public AliasList getAliasList(IdentifierCollection identifierCollection) + { + if(identifierCollection != null) + { + return getAliasList(identifierCollection.getAliasListConfiguration()); + } + + return null; + } + + /** + * Retrieves an alias list specified by the alias list configuration identifier + * + * @param configurationIdentifier containing the name of an alias list + * @return alias list or null. + */ + public AliasList getAliasList(AliasListConfigurationIdentifier configurationIdentifier) + { + if(configurationIdentifier != null && configurationIdentifier.isValid()) + { + return getAliasList(configurationIdentifier.getValue()); + } + + return null; + } + + /** + * Creates a new alias list containing all aliases that match the alias name, or returns a previously created and + * cached alias list. Returned alias list is automatically registered as a listener to this model so that any + * updates to the list by the user will automatically be reflected in constructed alias lists. + */ public AliasList getAliasList(String name) { - if(name != null && mAliasListMap.containsKey(name)) + if(name == null || name.isEmpty()) + { + return new AliasList(name); + } + + if(mAliasListMap.containsKey(name)) { return mAliasListMap.get(name); } AliasList aliasList = new AliasList(name); - if(name != null) + for(Alias alias : mAliases) { - for(Alias alias : mAliases) + if(alias.hasList() && alias.getList().equalsIgnoreCase(name)) { - if(alias.hasList() && alias.getList().equalsIgnoreCase(name)) - { - aliasList.addAlias(alias); - } + aliasList.addAlias(alias); } - - mAliasListMap.put(name, aliasList); - - //Register the new alias list to receive updates from this model - addListener(aliasList); } + mAliasListMap.put(name, aliasList); + + //Register the new alias list to receive updates from this model + addListener(aliasList); + return aliasList; } diff --git a/src/main/java/io/github/dsheirer/alias/PatchGroupAlias.java b/src/main/java/io/github/dsheirer/alias/PatchGroupAlias.java deleted file mode 100644 index 9350fec8..00000000 --- a/src/main/java/io/github/dsheirer/alias/PatchGroupAlias.java +++ /dev/null @@ -1,379 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.alias; - -import io.github.dsheirer.alias.action.AliasAction; -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; -import io.github.dsheirer.alias.id.priority.Priority; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -public class PatchGroupAlias extends Alias -{ - private final static Logger mLog = LoggerFactory.getLogger(PatchGroupAlias.class); - - private Alias mPatchGroupAlias; - private List mPatchedAliases = new ArrayList<>(); - private List mPatchedTalkgroupIDs = new ArrayList<>(); - - /** - * Patch group alias is a single alias for a group of aliases that are temporarily patched together. This will - * normally be used for patched talkgroups, but could be used with any type of alias identifiers that can be - * temporarily joined to form a patch group. - * - * Alias properties such as recordable, streamable and call priority are an aggregation from the set of patched - * aliases contained in this patch group alias and the patch group ID alias and where applicable, return the highest - * value/priority from the group of patched aliases. - */ - public PatchGroupAlias() - { - } - - @Override - public int getColor() - { - if(mPatchGroupAlias != null) - { - return mPatchGroupAlias.getColor(); - } - - return super.getColor(); - } - - /** - * Sets an alias for this patch group ID - */ - public void setPatchGroupAlias(Alias alias) - { - mPatchGroupAlias = alias; - } - - /** - * Alias for this patch group - */ - public Alias getPatchGroupAlias() - { - return mPatchGroupAlias; - } - - /** - * Indicates if this patch group has an alias - */ - public boolean hasPatchGroupAlias() - { - return mPatchGroupAlias != null; - } - - /** - * List of patched aliases contained in this patch group - */ - public List getPatchedAliases() - { - return mPatchedAliases; - } - - /** - * Removes/clears all patched aliases from this patch alias group - */ - public void clearPatchedAliases() - { - mPatchedAliases.clear(); - } - - /** - * Adds the patched alias to this patch alias group - */ - public void addPatchedAlias(Alias alias) - { - if(alias == PatchGroupAlias.this) - { - throw new IllegalArgumentException("Can't add patch group alias to itself - would create an infinite loop"); - } - - mPatchedAliases.add(alias); - } - - /** - * Removes the patched alias from this patch alias group - */ - public void removePatchedAlias(Alias alias) - { - mPatchedAliases.remove(alias); - } - - /** - * Sets the list of patched talkgroup IDs for this patch group - */ - public void setPatchedTalkgroupIDs(List talkgroupIDs) - { - mPatchedTalkgroupIDs = talkgroupIDs; - } - - /** - * List of patched talkgroup IDs for this patch group - */ - public List getPatchedTalkgroupIDs() - { - return mPatchedTalkgroupIDs; - } - - @Override - public String getName() - { - if(hasPatchGroupAlias()) - { - return getPatchGroupAlias().getName(); - } - else - { - for(AliasID id: getId()) - { - if(id.getType() == AliasIDType.TALKGROUP) - { - return "PATCH:" + ((TalkgroupID)id).getTalkgroup(); - } - } - } - - return "PATCH:****"; - } - - @Override - public List getAction() - { - List aliasActions = new ArrayList<>(); - - if(hasPatchGroupAlias()) - { - aliasActions.addAll(getPatchGroupAlias().getAction()); - } - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias)) - { - for(AliasAction action: alias.getAction()) - { - if(!aliasActions.contains(action)) - { - aliasActions.add(action); - } - } - } - } - - return aliasActions; - } - - @Override - public boolean hasActions() - { - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias) && alias.hasActions()) - { - return true; - } - } - - return hasPatchGroupAlias() && getPatchGroupAlias().hasActions(); - } - - /** - * Returns the highest listenable audio priority defined among the aliases in the group, or returns 'Do Not Monitor' - * if that is the only priority defined. Otherwise, returns default (100) priority. - */ - @Override - public int getCallPriority() - { - boolean hasDoNotMonitor = false; - - int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY + 1); - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority() && alias.getCallPriority() < highestPriority) - { - if(alias.getCallPriority() == Priority.DO_NOT_MONITOR) - { - hasDoNotMonitor = true; - } - else - { - highestPriority = alias.getCallPriority(); - } - } - } - - if(highestPriority <= Priority.DEFAULT_PRIORITY) - { - return highestPriority; - } - else if(hasDoNotMonitor) - { - return Priority.DO_NOT_MONITOR; - } - else - { - return Priority.DEFAULT_PRIORITY; - } - } - - @Override - public boolean hasCallPriority() - { - if(hasPatchGroupAlias() && getPatchGroupAlias().hasCallPriority()) - { - return true; - } - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority()) - { - return true; - } - } - - return false; - } - - @Override - public boolean isRecordable() - { - if(hasPatchGroupAlias() && getPatchGroupAlias().isRecordable()) - { - return true; - } - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias) && alias.isRecordable()) - { - return true; - } - } - - return false; - } - - @Override - public boolean isStreamable() - { - if(hasPatchGroupAlias() && getPatchGroupAlias().isStreamable()) - { - return true; - } - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias) && alias.isStreamable()) - { - return true; - } - } - - return false; - } - - @Override - public Set getBroadcastChannels() - { - Set broadcastChannels = new TreeSet<>(); - - if(hasPatchGroupAlias()) - { - broadcastChannels.addAll(getPatchGroupAlias().getBroadcastChannels()); - } - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias)) - { - broadcastChannels.addAll(alias.getBroadcastChannels()); - } - } - - return broadcastChannels; - } - - @Override - public boolean hasBroadcastChannel(String channel) - { - if(hasPatchGroupAlias() && getPatchGroupAlias().hasBroadcastChannel(channel)) - { - return true; - } - - for(Alias alias: mPatchedAliases) - { - if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel)) - { - return true; - } - } - - return false; - } - - public static void main(String[] args) - { - Alias a1 = new Alias("Alias 1"); - a1.addAliasID(new BroadcastChannel("Channel 1")); - a1.addAliasID(new BroadcastChannel("Channel 2")); - a1.addAliasID(new BroadcastChannel("Channel 3")); - a1.addAliasID(new BroadcastChannel("Channel 4")); - - Alias a2 = new Alias("Alias 1"); - a1.addAliasID(new BroadcastChannel("Channel 1")); - a1.addAliasID(new BroadcastChannel("Channel 2")); - a1.addAliasID(new BroadcastChannel("Channel 3")); - a1.addAliasID(new BroadcastChannel("Channel 4")); - - Alias a3 = new Alias("Alias 1"); - a1.addAliasID(new BroadcastChannel("Channel 1")); - a1.addAliasID(new BroadcastChannel("Channel 2")); - a1.addAliasID(new BroadcastChannel("Channel 3")); - a1.addAliasID(new BroadcastChannel("Channel 4")); - - Alias a4 = new Alias("Alias 1"); - a1.addAliasID(new BroadcastChannel("Channel 1")); - a1.addAliasID(new BroadcastChannel("Channel 2")); - a1.addAliasID(new BroadcastChannel("Channel 3")); - a1.addAliasID(new BroadcastChannel("Channel 4")); - - PatchGroupAlias pga = new PatchGroupAlias(); - pga.setPatchGroupAlias(a1); - pga.addPatchedAlias(a2); - pga.addPatchedAlias(a3); - pga.addPatchedAlias(a4); - - Set bc = pga.getBroadcastChannels(); - - mLog.debug("Channels:" + bc); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/action/AliasAction.java b/src/main/java/io/github/dsheirer/alias/action/AliasAction.java index ae514992..6018019d 100644 --- a/src/main/java/io/github/dsheirer/alias/action/AliasAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/AliasAction.java @@ -26,9 +26,7 @@ import io.github.dsheirer.alias.Alias; import io.github.dsheirer.alias.action.beep.BeepAction; import io.github.dsheirer.alias.action.clip.ClipAction; import io.github.dsheirer.alias.action.script.ScriptAction; -import io.github.dsheirer.message.Message; - -import java.util.concurrent.ScheduledExecutorService; +import io.github.dsheirer.message.IMessage; /** * Alias action defines an action to execute when an alias is detected active. @@ -56,7 +54,7 @@ public abstract class AliasAction * action attached. The alias argument is the parent alias containing the * alias action. */ - public abstract void execute(ScheduledExecutorService scheduledExecutorService, Alias alias, Message message); + public abstract void execute(Alias alias, IMessage message); /** * Dismiss a persistent alias action diff --git a/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java b/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java index 760122cb..8d73f0eb 100644 --- a/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java +++ b/src/main/java/io/github/dsheirer/alias/action/AliasActionManager.java @@ -19,51 +19,86 @@ package io.github.dsheirer.alias.action; import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.message.IMessageListener; +import io.github.dsheirer.module.Module; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.util.ThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; /** - * Manages all alias action events. Each received message is interrogated for - * any alias entries and then each alias is interrogated for any alias actions. + * Manages all alias action events. Each received message is interrogated for any identifiers that may be aliasable + * and have associated alias actions. * - * Each alias action is executed and provided a copy of the source message so - * that the contents of the message can be used as part of the action. + * Each alias action is executed and provided a copy of the source message so that the contents of the message can be + * used as part of the action. */ -public class AliasActionManager implements Listener +public class AliasActionManager extends Module implements IMessageListener, Listener { - public AliasActionManager() + private final static Logger mLog = LoggerFactory.getLogger(AliasActionManager.class); + private AliasList mAliasList; + + public AliasActionManager(AliasList aliasList) { + mAliasList = aliasList; } @Override - public void receive(Message message) + public void receive(IMessage message) { - if(message.isValid()) + if(mAliasList != null && message.isValid()) { - List aliases = message.getAliases(); + List identifiers = message.getIdentifiers(); - if(aliases != null) + for(Identifier identifier: identifiers) { - for(Alias alias : aliases) - { - if(alias.hasActions()) - { - List actions = alias.getAction(); + Alias alias = mAliasList.getAlias(identifier); - for(AliasAction action : actions) - { - /* Provide access to the thread pool manager in case the - * action needs to setup a timer, and provide the original - * message to be used as part of the action (e.g. sending - * the message as a text message to a cell phone */ - action.execute(ThreadPool.SCHEDULED, alias, message); - } + if(alias != null && alias.hasActions()) + { + for(AliasAction action: alias.getAction()) + { + action.execute(alias, message); } } } } } + + @Override + public void reset() + { + //No actions neeeded + } + + @Override + public void start() + { + //No actions neeeded + } + + @Override + public void stop() + { + //No actions neeeded + } + + @Override + public void dispose() + { + //No actions neeeded + } + + /** + * IMessageListener interface ... delegates to this class implementation of Listener + */ + @Override + public Listener getMessageListener() + { + return this; + } } diff --git a/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java b/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java index 8847eddf..5df29108 100644 --- a/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/RecurringAction.java @@ -1,13 +1,33 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.alias.action; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.util.ThreadPool; -import javax.swing.*; -import java.awt.*; -import java.util.concurrent.ScheduledExecutorService; +import javax.swing.JOptionPane; +import java.awt.EventQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -18,19 +38,15 @@ public abstract class RecurringAction extends AliasAction protected AtomicBoolean mRunning = new AtomicBoolean(false); @JsonIgnore private ScheduledFuture mPerpetualAction; - @JsonIgnore - protected ScheduledExecutorService mScheduledExecutorService; protected Interval mInterval = Interval.ONCE; protected int mPeriod = 5; - public abstract void performAction(Alias alias, Message message); + public abstract void performAction(Alias alias, IMessage message); @Override - public void execute(ScheduledExecutorService scheduledExecutorService, Alias alias, Message message) + public void execute(Alias alias, IMessage message) { - mScheduledExecutorService = scheduledExecutorService; - if(mRunning.compareAndSet(false, true)) { switch(mInterval) @@ -41,10 +57,10 @@ public abstract class RecurringAction extends AliasAction break; case DELAYED_RESET: performThreadedAction(alias, message); - mScheduledExecutorService.schedule(new ResetTask(), mPeriod, TimeUnit.SECONDS); + ThreadPool.SCHEDULED.schedule(new ResetTask(), mPeriod, TimeUnit.SECONDS); break; case UNTIL_DISMISSED: - mPerpetualAction = mScheduledExecutorService.scheduleAtFixedRate( + mPerpetualAction = ThreadPool.SCHEDULED.scheduleAtFixedRate( new PerformActionTask(alias, message), mPeriod, mPeriod, TimeUnit.SECONDS); StringBuilder sb = new StringBuilder(); @@ -56,18 +72,13 @@ public abstract class RecurringAction extends AliasAction final String text = sb.toString(); - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - JOptionPane.showMessageDialog(null, text, - "Alias Alert", JOptionPane.INFORMATION_MESSAGE); + EventQueue.invokeLater(() -> { + JOptionPane.showMessageDialog(null, text, + "Alias Alert", JOptionPane.INFORMATION_MESSAGE); - dismiss(false); + dismiss(false); - mScheduledExecutorService.schedule(new ResetTask(), 15, TimeUnit.SECONDS); - } + ThreadPool.SCHEDULED.schedule(new ResetTask(), 15, TimeUnit.SECONDS); }); break; default: @@ -79,16 +90,9 @@ public abstract class RecurringAction extends AliasAction * Spawns the performAction() event into a new thread so that it doesn't * delay any decoder actions. */ - private void performThreadedAction(final Alias alias, final Message message) + private void performThreadedAction(final Alias alias, final IMessage message) { - mScheduledExecutorService.schedule(new Runnable() - { - @Override - public void run() - { - performAction(alias, message); - } - }, 0, TimeUnit.SECONDS); + ThreadPool.SCHEDULED.schedule(() -> performAction(alias, message), 0, TimeUnit.SECONDS); } @Override @@ -139,7 +143,7 @@ public abstract class RecurringAction extends AliasAction private String mLabel; - private Interval(String label) + Interval(String label) { mLabel = label; } @@ -153,9 +157,9 @@ public abstract class RecurringAction extends AliasAction public class PerformActionTask implements Runnable { private Alias mAlias; - private Message mMessage; + private IMessage mMessage; - public PerformActionTask(Alias alias, Message message) + public PerformActionTask(Alias alias, IMessage message) { mAlias = alias; mMessage = message; diff --git a/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java b/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java index 6a0717ac..644b655d 100644 --- a/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java @@ -1,10 +1,30 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.alias.action.beep; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.Alias; import io.github.dsheirer.alias.action.AliasActionType; import io.github.dsheirer.alias.action.RecurringAction; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; public class BeepAction extends RecurringAction { @@ -17,7 +37,7 @@ public class BeepAction extends RecurringAction } @Override - public void performAction(Alias alias, Message message ) + public void performAction(Alias alias, IMessage message ) { System.out.println( "\007" ); } diff --git a/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java b/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java index 15807304..9bea3933 100644 --- a/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/clip/ClipAction.java @@ -1,3 +1,23 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.alias.action.clip; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -5,7 +25,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.Alias; import io.github.dsheirer.alias.action.AliasActionType; import io.github.dsheirer.alias.action.RecurringAction; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +65,7 @@ public class ClipAction extends RecurringAction } @Override - public void performAction(Alias alias, Message message) + public void performAction(Alias alias, IMessage message) { try { diff --git a/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java b/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java index 9aa920c2..e837010d 100644 --- a/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/script/ScriptAction.java @@ -1,10 +1,30 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.alias.action.script; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.Alias; import io.github.dsheirer.alias.action.AliasActionType; import io.github.dsheirer.alias.action.RecurringAction; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +59,7 @@ public class ScriptAction extends RecurringAction } @Override - public void performAction(Alias alias, Message message) + public void performAction(Alias alias, IMessage message) { try { @@ -63,8 +83,7 @@ public class ScriptAction extends RecurringAction int exitCode = p.waitFor(); - BufferedReader reader = new BufferedReader( - new InputStreamReader(p.getInputStream())); + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = ""; @@ -77,8 +96,7 @@ public class ScriptAction extends RecurringAction if(exitCode != 0) { - throw new RuntimeException("Exit Code: " + exitCode + - " Console:" + sb.toString()); + throw new RuntimeException("Exit Code: " + exitCode + " Console:" + sb.toString()); } } } diff --git a/src/main/java/io/github/dsheirer/alias/id/AliasID.java b/src/main/java/io/github/dsheirer/alias/id/AliasID.java index 0534f4a2..935c764e 100644 --- a/src/main/java/io/github/dsheirer/alias/id/AliasID.java +++ b/src/main/java/io/github/dsheirer/alias/id/AliasID.java @@ -1,17 +1,17 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014-2016 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 ******************************************************************************/ @@ -23,46 +23,52 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; import io.github.dsheirer.alias.id.esn.Esn; -import io.github.dsheirer.alias.id.fleetsync.FleetsyncID; +import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID; +import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID; +import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID; +import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable; +import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID; import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID; -import io.github.dsheirer.alias.id.mdc.MDC1200ID; import io.github.dsheirer.alias.id.mobileID.Min; -import io.github.dsheirer.alias.id.mpt1327.MPT1327ID; -import io.github.dsheirer.alias.id.nonrecordable.NonRecordable; import io.github.dsheirer.alias.id.priority.Priority; +import io.github.dsheirer.alias.id.record.Record; import io.github.dsheirer.alias.id.siteID.SiteID; import io.github.dsheirer.alias.id.status.StatusID; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupID; +import io.github.dsheirer.alias.id.talkgroup.Talkgroup; +import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange; import io.github.dsheirer.alias.id.uniqueID.UniqueID; -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.PROPERTY, property="type") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ - @JsonSubTypes.Type(value=BroadcastChannel.class, name="broadcastChannel"), - @JsonSubTypes.Type(value=Esn.class, name="esn"), - @JsonSubTypes.Type(value=FleetsyncID.class, name="fleetsyncID"), + @JsonSubTypes.Type(value = BroadcastChannel.class, name = "broadcastChannel"), + @JsonSubTypes.Type(value = Esn.class, name = "esn"), + @JsonSubTypes.Type(value = FleetsyncID.class, name = "fleetsyncID"), @JsonSubTypes.Type(value = LoJackFunctionAndID.class, name = "loJackFunctionAndID"), @JsonSubTypes.Type(value = MDC1200ID.class, name = "mdc1200ID"), @JsonSubTypes.Type(value = Min.class, name = "min"), @JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"), @JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"), + @JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"), + @JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"), @JsonSubTypes.Type(value = Priority.class, name = "priority"), + @JsonSubTypes.Type(value = Record.class, name = "record"), @JsonSubTypes.Type(value = SiteID.class, name = "siteID"), @JsonSubTypes.Type(value = StatusID.class, name = "statusID"), - @JsonSubTypes.Type(value = TalkgroupID.class, name = "talkgroupID"), + @JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"), @JsonSubTypes.Type(value = UniqueID.class, name = "uniqueID") }) -@JacksonXmlRootElement( localName = "id" ) +@JacksonXmlRootElement(localName = "id") public abstract class AliasID { - public AliasID() - { - } + public AliasID() + { + } - @JsonIgnore - public abstract AliasIDType getType(); - - public abstract boolean matches( AliasID id ); + @JsonIgnore + public abstract AliasIDType getType(); - @JsonIgnore - public abstract boolean isValid(); + public abstract boolean matches(AliasID id); + + @JsonIgnore + public abstract boolean isValid(); } diff --git a/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java b/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java index bd95471e..206a9e09 100644 --- a/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java +++ b/src/main/java/io/github/dsheirer/alias/id/AliasIDType.java @@ -1,49 +1,55 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.alias.id; public enum AliasIDType { - ESN( "ESN" ), - FLEETSYNC( "Fleetsync" ), - LOJACK( "LoJack" ), - LTR_NET_UID( "LTR-Net UID" ), - MDC1200( "MDC-1200" ), - MIN( "Passport MIN" ), - MPT1327( "MPT-1327" ), - SITE( "Site" ), - STATUS( "Status" ), - TALKGROUP( "Talkgroup" ), + BROADCAST_CHANNEL("Audio Broadcast Channel"), + ESN("ESN"), + INVERT("Audio Inversion"), + LOJACK("LoJack"), + LTR_NET_UID("LTR-Net UID"), + MIN("Passport MIN"), + PRIORITY("Audio Priority"), + RECORD("Record"), + SITE("Site"), + STATUS("Status"), + TALKGROUP("Talkgroup"), + TALKGROUP_RANGE("Talkgroup Range"), - INVERT( "Audio Inversion" ), - PRIORITY( "Audio Priority" ), - NON_RECORDABLE( "Audio Non-Recordable" ), - BROADCAST_CHANNEL( "Audio Broadcast Channel" ); - - private String mLabel; - - private AliasIDType( String label ) - { - mLabel = label; - } - - public String toString() - { - return mLabel; - } + //Legacy identifier types - no longer used + FLEETSYNC("Fleetsync"), + LEGACY_TALKGROUP("Legacy Talkgroup"), + MDC1200("MDC-1200"), + MPT1327("MPT-1327"), + NON_RECORDABLE("Audio Non-Recordable"); + + private String mLabel; + + AliasIDType(String label) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java b/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java index d0d68901..e6bb7b9d 100644 --- a/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java +++ b/src/main/java/io/github/dsheirer/alias/id/AliasIdentifierEditor.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.alias.id; import io.github.dsheirer.alias.Alias; @@ -26,10 +28,19 @@ import io.github.dsheirer.gui.editor.Editor; import io.github.dsheirer.gui.editor.EmptyEditor; import net.miginfocom.swing.MigLayout; -import javax.swing.*; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import java.awt.*; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -40,328 +51,327 @@ import java.util.List; public class AliasIdentifierEditor extends Editor { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private static ListModel EMPTY_MODEL = new DefaultListModel<>(); - private JList mAliasIDList = new JList<>( EMPTY_MODEL ); - private JButton mNewIDButton; - private JButton mCloneIDButton; - private JButton mDeleteIDButton; - private EditorContainer mEditorContainer = new EditorContainer(); + private static ListModel EMPTY_MODEL = new DefaultListModel<>(); + private JList mAliasIDList = new JList<>(EMPTY_MODEL); + private JButton mNewIDButton; + private JButton mCloneIDButton; + private JButton mDeleteIDButton; + private EditorContainer mEditorContainer = new EditorContainer(); - private AliasModel mAliasModel; + private AliasModel mAliasModel; private BroadcastModel mBroadcastModel; - - public AliasIdentifierEditor( AliasModel aliasModel, BroadcastModel broadcastModel ) - { - mAliasModel = aliasModel; + + public AliasIdentifierEditor(AliasModel aliasModel, BroadcastModel broadcastModel) + { + mAliasModel = aliasModel; mBroadcastModel = broadcastModel; - init(); - } + init(); + } - private void init() - { - setLayout( new MigLayout( "fill,wrap 3", - "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); + private void init() + { + setLayout(new MigLayout("fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]")); - mAliasIDList.setVisibleRowCount( 6 ); - mAliasIDList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - mAliasIDList.setLayoutOrientation( JList.VERTICAL ); - mAliasIDList.addListSelectionListener( new ListSelectionListener() - { - @Override - public void valueChanged( ListSelectionEvent event ) - { - if( !event.getValueIsAdjusting() ) - { - JList list = (JList)event.getSource(); - - Object selectedItem = list.getSelectedValue(); - - if( selectedItem != null && selectedItem instanceof AliasID ) - { - AliasID selected = (AliasID)selectedItem; + mAliasIDList.setVisibleRowCount(6); + mAliasIDList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + mAliasIDList.setLayoutOrientation(JList.VERTICAL); + mAliasIDList.addListSelectionListener(new ListSelectionListener() + { + @Override + public void valueChanged(ListSelectionEvent event) + { + if(!event.getValueIsAdjusting()) + { + JList list = (JList)event.getSource(); - mEditorContainer.setAliasID( selected ); - - mCloneIDButton.setEnabled( true ); - mDeleteIDButton.setEnabled( true ); - } - else - { - mCloneIDButton.setEnabled( false ); - mDeleteIDButton.setEnabled( false ); - } - } - } - } ); - - JScrollPane scroller = new JScrollPane( mAliasIDList ); - add( scroller, "span,grow" ); - - add( mEditorContainer, "span,grow" ); + Object selectedItem = list.getSelectedValue(); - mNewIDButton = new JButton( "New ..." ); - mNewIDButton.setToolTipText( "Create a new Alias Identifier" ); - mNewIDButton.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JPopupMenu menu = new JPopupMenu(); + if(selectedItem != null && selectedItem instanceof AliasID) + { + AliasID selected = (AliasID)selectedItem; - menu.add( new AddAliasIdentifierItem( AliasIDType.ESN ) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.FLEETSYNC) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.LOJACK) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.LTR_NET_UID) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.MDC1200 ) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.MPT1327 ) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.MIN ) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.SITE) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.STATUS) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.TALKGROUP) ); + mEditorContainer.setAliasID(selected); - menu.addSeparator(); + mCloneIDButton.setEnabled(true); + mDeleteIDButton.setEnabled(true); + } + else + { + mCloneIDButton.setEnabled(false); + mDeleteIDButton.setEnabled(false); + } + } + } + }); - menu.add( new AddAliasIdentifierItem( AliasIDType.BROADCAST_CHANNEL) ); + JScrollPane scroller = new JScrollPane(mAliasIDList); + add(scroller, "span,grow"); - if( hasItem() ) - { - Alias alias = getItem(); + add(mEditorContainer, "span,grow"); - if( alias.isRecordable() ) - { - menu.add( new AddAliasIdentifierItem( AliasIDType.NON_RECORDABLE) ); - } + mNewIDButton = new JButton("New ..."); + mNewIDButton.setToolTipText("Create a new Alias Identifier"); + mNewIDButton.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + JPopupMenu menu = new JPopupMenu(); - if( !alias.hasCallPriority() ) - { - menu.add( new AddAliasIdentifierItem( AliasIDType.PRIORITY) ); - } - } + menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP)); + menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP_RANGE)); + menu.add(new AddAliasIdentifierItem(AliasIDType.ESN)); + menu.add(new AddAliasIdentifierItem(AliasIDType.LOJACK)); + menu.add(new AddAliasIdentifierItem(AliasIDType.LTR_NET_UID)); + menu.add(new AddAliasIdentifierItem(AliasIDType.MDC1200)); + menu.add(new AddAliasIdentifierItem(AliasIDType.MIN)); + menu.add(new AddAliasIdentifierItem(AliasIDType.SITE)); + menu.add(new AddAliasIdentifierItem(AliasIDType.STATUS)); - menu.show( e.getComponent(), e.getX(), e.getY() ); - } - } ); - - add( mNewIDButton ); - - mCloneIDButton = new JButton( "Clone" ); - mCloneIDButton.setToolTipText( "Create a copy of the currently selected identifier" ); - mCloneIDButton.setEnabled( false ); - mCloneIDButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AliasID selected = mAliasIDList.getSelectedValue(); - - if( selected != null ) - { - AliasID clone = AliasFactory.copyOf( selected ); + menu.addSeparator(); - if( clone != null ) - { - addAliasIDToList( clone ); - } - } - } - } ); - add( mCloneIDButton ); - - mDeleteIDButton = new JButton( "Delete" ); - mDeleteIDButton.setToolTipText( "Delete the currently selected identifier" ); - mDeleteIDButton.setEnabled( false ); - mDeleteIDButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final AliasID selected = mAliasIDList.getSelectedValue(); - - if( selected != null ) - { - int choice = JOptionPane.showConfirmDialog( mDeleteIDButton, - "Do you want to delete [" + selected.toString() + "]", - "Are you sure?", JOptionPane.YES_NO_OPTION ); - - if( choice == JOptionPane.YES_OPTION && hasItem() ) - { - Alias alias = getItem(); - - alias.removeAliasID( selected ); - - mAliasModel.broadcast( new AliasEvent( alias, AliasEvent.Event.CHANGE ) ); + menu.add(new AddAliasIdentifierItem(AliasIDType.BROADCAST_CHANNEL)); - setItem( alias ); - } - } - } - } ); - add( mDeleteIDButton, "wrap" ); - } - - @Override - public void setItem( Alias alias ) - { - super.setItem( alias ); - - if( alias == null || alias.getId().isEmpty() ) - { - mAliasIDList.setModel( EMPTY_MODEL ); - } - else - { - DefaultListModel model = new DefaultListModel(); + if(hasItem()) + { + Alias alias = getItem(); - List ids = alias.getId(); - - Collections.sort( ids, new Comparator() - { - @Override - public int compare( AliasID o1, AliasID o2 ) - { - return o1.toString().compareTo( o2.toString() ); - } - } ); - - for( AliasID id: ids ) - { - model.addElement( id ); - } - - mAliasIDList.setModel( model ); - } - - mEditorContainer.setAliasID( null ); - } - - @Override - public void save() - { - if( isModified() || mEditorContainer.isModified() ) - { - if( mEditorContainer.isModified() ) - { - mEditorContainer.save(); - } - - setModified( false ); - - if( hasItem() ) - { - mAliasModel.broadcast( new AliasEvent( getItem(), AliasEvent.Event.CHANGE ) ); - } - } - } - - public class EditorContainer extends JPanel - { - private static final long serialVersionUID = 1L; + if(!alias.isRecordable()) + { + menu.add(new AddAliasIdentifierItem(AliasIDType.RECORD)); + } - private Editor mEditor = new EmptyEditor<>(); + if(!alias.hasCallPriority()) + { + menu.add(new AddAliasIdentifierItem(AliasIDType.PRIORITY)); + } + } - public EditorContainer() - { - setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) ); + menu.show(e.getComponent(), e.getX(), e.getY()); + } + }); - add( mEditor ); - } - - public boolean isModified() - { - if( mEditor != null ) - { - return mEditor.isModified(); - } - - return false; - } - - public void save() - { - if( mEditor != null ) - { - mEditor.save(); - } - } - - public void setAliasID( AliasID aliasID ) - { - if( mEditor != null ) - { - if( mEditor.isModified() ) - { - int option = JOptionPane.showConfirmDialog( - EditorContainer.this, - "Identifier settings have changed. Do you want to save these changes?", - "Save Changes?", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE ); - - if( option == JOptionPane.YES_OPTION ) - { - mEditor.save(); - } - } - } - removeAll(); - - //This will always give us an editor - mEditor = AliasFactory.getEditor( aliasID, mBroadcastModel ); - - add( mEditor ); + add(mNewIDButton); - revalidate(); - } - } - - private void addAliasIDToList( final AliasID id ) - { - if( id != null && hasItem() ) - { - final Alias alias = getItem(); - - alias.addAliasID( id ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - setItem( alias ); - - mAliasIDList.setSelectedValue( id, true ); - - AliasIdentifierEditor.this.setModified( true ); - } - } ); - } - } - - public class AddAliasIdentifierItem extends JMenuItem - { - private static final long serialVersionUID = 1L; - - private AliasIDType mAliasIDType; + mCloneIDButton = new JButton("Clone"); + mCloneIDButton.setToolTipText("Create a copy of the currently selected identifier"); + mCloneIDButton.setEnabled(false); + mCloneIDButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + AliasID selected = mAliasIDList.getSelectedValue(); - public AddAliasIdentifierItem( AliasIDType type ) - { - super( type.toString() ); - - mAliasIDType = type; - - addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final AliasID id = AliasFactory.getAliasID( mAliasIDType ); - - addAliasIDToList( id ); - } - } ); - } - } + if(selected != null) + { + AliasID clone = AliasFactory.copyOf(selected); + + if(clone != null) + { + addAliasIDToList(clone); + } + } + } + }); + add(mCloneIDButton); + + mDeleteIDButton = new JButton("Delete"); + mDeleteIDButton.setToolTipText("Delete the currently selected identifier"); + mDeleteIDButton.setEnabled(false); + mDeleteIDButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + final AliasID selected = mAliasIDList.getSelectedValue(); + + if(selected != null) + { + int choice = JOptionPane.showConfirmDialog(mDeleteIDButton, + "Do you want to delete [" + selected.toString() + "]", + "Are you sure?", JOptionPane.YES_NO_OPTION); + + if(choice == JOptionPane.YES_OPTION && hasItem()) + { + Alias alias = getItem(); + + alias.removeAliasID(selected); + + mAliasModel.broadcast(new AliasEvent(alias, AliasEvent.Event.CHANGE)); + + setItem(alias); + } + } + } + }); + add(mDeleteIDButton, "wrap"); + } + + @Override + public void setItem(Alias alias) + { + super.setItem(alias); + + if(alias == null || alias.getId().isEmpty()) + { + mAliasIDList.setModel(EMPTY_MODEL); + } + else + { + DefaultListModel model = new DefaultListModel(); + + List ids = alias.getId(); + + Collections.sort(ids, new Comparator() + { + @Override + public int compare(AliasID o1, AliasID o2) + { + return o1.toString().compareTo(o2.toString()); + } + }); + + for(AliasID id : ids) + { + model.addElement(id); + } + + mAliasIDList.setModel(model); + } + + mEditorContainer.setAliasID(null); + } + + @Override + public void save() + { + if(isModified() || mEditorContainer.isModified()) + { + if(mEditorContainer.isModified()) + { + mEditorContainer.save(); + } + + setModified(false); + + if(hasItem()) + { + mAliasModel.broadcast(new AliasEvent(getItem(), AliasEvent.Event.CHANGE)); + } + } + } + + public class EditorContainer extends JPanel + { + private static final long serialVersionUID = 1L; + + private Editor mEditor = new EmptyEditor<>(); + + public EditorContainer() + { + setLayout(new MigLayout("", "[grow,fill]", "[grow,fill]")); + + add(mEditor); + } + + public boolean isModified() + { + if(mEditor != null) + { + return mEditor.isModified(); + } + + return false; + } + + public void save() + { + if(mEditor != null) + { + mEditor.save(); + } + } + + public void setAliasID(AliasID aliasID) + { + if(mEditor != null) + { + if(mEditor.isModified()) + { + int option = JOptionPane.showConfirmDialog( + EditorContainer.this, + "Identifier settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + + if(option == JOptionPane.YES_OPTION) + { + mEditor.save(); + } + } + } + removeAll(); + + //This will always give us an editor + mEditor = AliasFactory.getEditor(aliasID, mBroadcastModel); + + add(mEditor); + + revalidate(); + } + } + + private void addAliasIDToList(final AliasID id) + { + if(id != null && hasItem()) + { + final Alias alias = getItem(); + + alias.addAliasID(id); + + EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + setItem(alias); + + mAliasIDList.setSelectedValue(id, true); + + AliasIdentifierEditor.this.setModified(true); + } + }); + } + } + + public class AddAliasIdentifierItem extends JMenuItem + { + private static final long serialVersionUID = 1L; + + private AliasIDType mAliasIDType; + + public AddAliasIdentifierItem(AliasIDType type) + { + super(type.toString()); + + mAliasIDType = type; + + addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + final AliasID id = AliasFactory.getAliasID(mAliasIDType); + + addAliasIDToList(id); + } + }); + } + } } diff --git a/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java b/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java index 656c7793..a2b66022 100644 --- a/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java +++ b/src/main/java/io/github/dsheirer/alias/id/esn/Esn.java @@ -48,7 +48,13 @@ public class Esn extends AliasID public String toString() { - return "ESN: " + mEsn; + StringBuilder sb = new StringBuilder(); + sb.append("ESN: ").append(mEsn != null ? mEsn : "(empty)"); + if(!isValid()) + { + sb.append(" **NOT VALID**"); + } + return sb.toString(); } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/fleetsync/FleetsyncID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java similarity index 53% rename from src/main/java/io/github/dsheirer/alias/id/fleetsync/FleetsyncID.java rename to src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java index 19473bdf..3076d3b7 100644 --- a/src/main/java/io/github/dsheirer/alias/id/fleetsync/FleetsyncID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncID.java @@ -1,21 +1,23 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package io.github.dsheirer.alias.id.fleetsync; +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.fleetsync; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.id.AliasID; @@ -48,7 +50,7 @@ public class FleetsyncID extends AliasID public String toString() { - return "Fleetsync: " + mID; + return "Fleetsync: " + (mID != null ? mID : "(empty)") + " **INVALID - USE TALKGROUP INSTEAD"; } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/fleetsync/FleetsyncIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java similarity index 69% rename from src/main/java/io/github/dsheirer/alias/id/fleetsync/FleetsyncIDEditor.java rename to src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java index 89fe739c..85f7a2ba 100644 --- a/src/main/java/io/github/dsheirer/alias/id/fleetsync/FleetsyncIDEditor.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/fleetsync/FleetsyncIDEditor.java @@ -1,21 +1,23 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.alias.id.fleetsync; +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.fleetsync; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.gui.editor.DocumentListenerEditor; @@ -23,9 +25,13 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; import javax.swing.text.MaskFormatter; -import java.awt.*; +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; diff --git a/src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200ID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java similarity index 56% rename from src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200ID.java rename to src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java index e8f50df7..85dc60d9 100644 --- a/src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200ID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200ID.java @@ -1,21 +1,23 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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.alias.id.mdc; + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.mdc; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; @@ -49,7 +51,7 @@ public class MDC1200ID extends AliasID public String toString() { - return "MDC-1200: " + mID; + return "MDC-1200: " + (mID != null ? mID : "(empty)") + " **INVALID - USE TALKGROUP INSTEAD**"; } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java new file mode 100644 index 00000000..f472e6d5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mdc/MDC1200IDEditor.java @@ -0,0 +1,133 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.mdc; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.gui.editor.DocumentListenerEditor; +import net.miginfocom.swing.MigLayout; + +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class MDC1200IDEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "" + + "

MDC-1200 Identifier

" + + "MDC-1200: four digit [0-9] value (e.g. 1234)
" + + "Wildcard: use an asterisk (*) to wildcard individual
" + + "digits (e.g. 123* or **34)" + + ""; + + private JTextField mTextField; + + public MDC1200IDEditor(AliasID aliasID) + { + initGUI(); + + setItem(aliasID); + } + + private void initGUI() + { + setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); + + add(new JLabel("MDC-1200 ID:")); + + MaskFormatter formatter = null; + + try + { + //Mask: 4 digits + formatter = new MaskFormatter("####"); + } + catch(Exception e) + { + //Do nothing, the mask was invalid + } + + mTextField = new JFormattedTextField(formatter); + mTextField.getDocument().addDocumentListener(this); + mTextField.setToolTipText(HELP_TEXT); + add(mTextField, "growx,push"); + + JLabel help = new JLabel("Help ..."); + help.setForeground(Color.BLUE.brighter()); + help.setCursor(new Cursor(Cursor.HAND_CURSOR)); + help.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + JOptionPane.showMessageDialog(MDC1200IDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); + } + }); + add(help, "align left"); + } + + public MDC1200ID getMDC1200ID() + { + if(getItem() instanceof MDC1200ID) + { + return (MDC1200ID)getItem(); + } + + return null; + } + + @Override + public void setItem(AliasID aliasID) + { + super.setItem(aliasID); + + MDC1200ID mdc = getMDC1200ID(); + + if(mdc != null) + { + mTextField.setText(mdc.getIdent()); + } + + setModified(false); + + repaint(); + } + + @Override + public void save() + { + MDC1200ID mdc = getMDC1200ID(); + + if(mdc != null) + { + mdc.setIdent(mTextField.getText()); + } + + setModified(false); + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/mpt1327/MPT1327ID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java similarity index 55% rename from src/main/java/io/github/dsheirer/alias/id/mpt1327/MPT1327ID.java rename to src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java index 53208bbb..b27ad6ee 100644 --- a/src/main/java/io/github/dsheirer/alias/id/mpt1327/MPT1327ID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327ID.java @@ -1,21 +1,23 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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.alias.id.mpt1327; + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.mpt1327; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; @@ -49,7 +51,7 @@ public class MPT1327ID extends AliasID public String toString() { - return "MPT-1327: " + mID; + return "MPT-1327: " + (mID != null ? mID : "(empty)") + " **INVALID - USE TALKGROUP INSTEAD**"; } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/mpt1327/MPT1327IDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java similarity index 68% rename from src/main/java/io/github/dsheirer/alias/id/mpt1327/MPT1327IDEditor.java rename to src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java index 6ce3ccad..36c38ecc 100644 --- a/src/main/java/io/github/dsheirer/alias/id/mpt1327/MPT1327IDEditor.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/mpt1327/MPT1327IDEditor.java @@ -1,29 +1,35 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.alias.id.mpt1327; +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.mpt1327; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.gui.editor.DocumentListenerEditor; import net.miginfocom.swing.MigLayout; -import javax.swing.*; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; import javax.swing.text.MaskFormatter; -import java.awt.*; +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java new file mode 100644 index 00000000..b9d10706 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordable.java @@ -0,0 +1,53 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.alias.id.legacy.nonrecordable; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; + +public class NonRecordable extends AliasID +{ + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @Override + public AliasIDType getType() + { + return AliasIDType.NON_RECORDABLE; + } + + @Override + public boolean isValid() + { + return true; + } + + @Override + public boolean matches(AliasID id) + { + return false; + } + + @Override + public String toString() + { + return "Audio Non-Recordable - **INVALID - USE RECORD INSTEAD**"; + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java new file mode 100644 index 00000000..51a3438a --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/nonrecordable/NonRecordableEditor.java @@ -0,0 +1,78 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.nonrecordable; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.gui.editor.DocumentListenerEditor; +import net.miginfocom.swing.MigLayout; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class NonRecordableEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "" + + "

Audio Non-Recordable

" + + "This identifies an alias as non-recordable.

" + + "The alias can still be monitored but associated audio will
" + + "not be recorded.

" + + "The default behavior when audio recording is enabled for a
" + + "channel is to record all aliases except any alias with a
" + + "non-recordable identifier"; + + public NonRecordableEditor(AliasID aliasID) + { + initGUI(); + + setItem(aliasID); + } + + private void initGUI() + { + setLayout(new MigLayout("fill,wrap 1", "[]", "[][]")); + + add(new JLabel("Non-Recordable"), "wrap"); + + JLabel help = new JLabel("Help ..."); + help.setForeground(Color.BLUE.brighter()); + help.setCursor(new Cursor(Cursor.HAND_CURSOR)); + help.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + JOptionPane.showMessageDialog(NonRecordableEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); + } + }); + add(help, "align left"); + } + + @Override + public void save() + { + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupID.java b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java similarity index 50% rename from src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupID.java rename to src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java index c70accbd..bc925e83 100644 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupID.java +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupID.java @@ -1,37 +1,39 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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.alias.id.talkgroup; + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.talkgroup; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.alias.id.AliasID; import io.github.dsheirer.alias.id.AliasIDType; -public class TalkgroupID extends AliasID +public class LegacyTalkgroupID extends AliasID { private String mTalkgroup; - public TalkgroupID() + public LegacyTalkgroupID() { //No arg JAXB constructor } - public TalkgroupID(String talkgroup) + public LegacyTalkgroupID(String talkgroup) { mTalkgroup = talkgroup; } @@ -57,7 +59,7 @@ public class TalkgroupID extends AliasID { StringBuilder sb = new StringBuilder(); - sb.append("Talkgroup: " + mTalkgroup); + sb.append("Legacy Talkgroup:").append(mTalkgroup).append(" *Disabled - Please Convert"); return sb.toString(); } @@ -67,9 +69,9 @@ public class TalkgroupID extends AliasID { boolean retVal = false; - if(id instanceof TalkgroupID) + if(id instanceof LegacyTalkgroupID) { - TalkgroupID tgid = (TalkgroupID)id; + LegacyTalkgroupID tgid = (LegacyTalkgroupID)id; //Create a pattern - replace * wildcards with regex single-char wildcard String pattern = mTalkgroup.replace("*", ".?"); @@ -84,6 +86,6 @@ public class TalkgroupID extends AliasID @Override public AliasIDType getType() { - return AliasIDType.TALKGROUP; + return AliasIDType.LEGACY_TALKGROUP; } } diff --git a/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java new file mode 100644 index 00000000..7d88b587 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/legacy/talkgroup/LegacyTalkgroupIDEditor.java @@ -0,0 +1,121 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.legacy.talkgroup; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.gui.editor.DocumentListenerEditor; +import net.miginfocom.swing.MigLayout; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class LegacyTalkgroupIDEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = + "

Talkgroup Identifier

" + + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)
" + + "LTR: A-HH-TTT where A=Area H=Home T=Talkgroup (0-01-128)
" + + "Passport: 5-digit number (12345 or 00023)
" + + "
" + + "Wildcard: use an asterisk (*) in place of each talkgroup digit (e.g. 0*1*5)" + + ""; + + private JTextField mTextField; + + public LegacyTalkgroupIDEditor(AliasID aliasID) + { + initGUI(); + + setItem(aliasID); + } + + private void initGUI() + { + setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); + + add(new JLabel("Talkgroup:")); + + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener(this); + mTextField.setToolTipText(HELP_TEXT); + add(mTextField, "growx,push"); + + JLabel help = new JLabel("Help ..."); + help.setForeground(Color.BLUE.brighter()); + help.setCursor(new Cursor(Cursor.HAND_CURSOR)); + help.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + JOptionPane.showMessageDialog(LegacyTalkgroupIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); + } + }); + add(help, "align left"); + } + + public LegacyTalkgroupID getTalkgroupID() + { + if(getItem() instanceof LegacyTalkgroupID) + { + return (LegacyTalkgroupID)getItem(); + } + + return null; + } + + @Override + public void setItem(AliasID aliasID) + { + super.setItem(aliasID); + + LegacyTalkgroupID talkgroup = getTalkgroupID(); + + if(talkgroup != null) + { + mTextField.setText(talkgroup.getTalkgroup()); + } + + setModified(false); + + repaint(); + } + + @Override + public void save() + { + LegacyTalkgroupID talkgroup = getTalkgroupID(); + + if(talkgroup != null) + { + talkgroup.setTalkgroup(mTextField.getText()); + } + + setModified(false); + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java b/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java index 95f15688..8ec2802e 100644 --- a/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java +++ b/src/main/java/io/github/dsheirer/alias/id/lojack/LoJackFunctionAndID.java @@ -26,7 +26,7 @@ import io.github.dsheirer.module.decode.lj1200.LJ1200Message; public class LoJackFunctionAndID extends AliasID { private LJ1200Message.Function mFunction = LJ1200Message.Function.F0_UNKNOWN; - private String mID = "*****"; + private String mID = null; public LoJackFunctionAndID() { @@ -62,7 +62,8 @@ public class LoJackFunctionAndID extends AliasID public String toString() { - return "LoJack FUNC: " + mFunction.getLabel() + " ID:" + (mID == null ? "" : mID); + return "LoJack FUNCTION: " + mFunction.getLabel() + + " ID:" + (mID == null ? "" : mID) + (isValid() ? "" : " **NOT VALID**"); } /** diff --git a/src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200IDEditor.java b/src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200IDEditor.java deleted file mode 100644 index e688421e..00000000 --- a/src/main/java/io/github/dsheirer/alias/id/mdc/MDC1200IDEditor.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.alias.id.mdc; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import javax.swing.text.MaskFormatter; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class MDC1200IDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

MDC-1200 Identifier

" - + "MDC-1200: four digit [0-9] value (e.g. 1234)
" - + "Wildcard: use an asterisk (*) to wildcard individual
" - + "digits (e.g. 123* or **34)" - + ""; - - private JTextField mTextField; - - public MDC1200IDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "MDC-1200 ID:" ) ); - - MaskFormatter formatter = null; - - try - { - //Mask: 4 digits - formatter = new MaskFormatter( "####" ); - } - catch( Exception e ) - { - //Do nothing, the mask was invalid - } - - mTextField = new JFormattedTextField( formatter ); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( MDC1200IDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public MDC1200ID getMDC1200ID() - { - if( getItem() instanceof MDC1200ID ) - { - return (MDC1200ID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - MDC1200ID mdc = getMDC1200ID(); - - if( mdc != null ) - { - mTextField.setText( mdc.getIdent() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - MDC1200ID mdc = getMDC1200ID(); - - if( mdc != null ) - { - mdc.setIdent( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/mobileID/Min.java b/src/main/java/io/github/dsheirer/alias/id/mobileID/Min.java index aec0eb72..89282fc6 100644 --- a/src/main/java/io/github/dsheirer/alias/id/mobileID/Min.java +++ b/src/main/java/io/github/dsheirer/alias/id/mobileID/Min.java @@ -52,7 +52,7 @@ public class Min extends AliasID public String toString() { - return "MIN: " + mMin; + return "MIN: " + (mMin != null ? mMin : "(empty)") + (isValid() ? "" : " **NOT VALID**"); } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordable.java b/src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordable.java deleted file mode 100644 index b6a2fcb9..00000000 --- a/src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordable.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.dsheirer.alias.id.nonrecordable; - -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.alias.id.AliasIDType; - -public class NonRecordable extends AliasID -{ - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") - @Override - public AliasIDType getType() - { - return AliasIDType.NON_RECORDABLE; - } - - @Override - public boolean isValid() - { - return true; - } - - @Override - public boolean matches(AliasID id) - { - return false; - } - - @Override - public String toString() - { - return "Audio Non-Recordable"; - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordableEditor.java b/src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordableEditor.java deleted file mode 100644 index 02726cc9..00000000 --- a/src/main/java/io/github/dsheirer/alias/id/nonrecordable/NonRecordableEditor.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.alias.id.nonrecordable; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class NonRecordableEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = "" - + "

Audio Non-Recordable

" - + "This identifies an alias as non-recordable.

" - + "The alias can still be monitored but associated audio will
" - + "not be recorded.

" - + "The default behavior when audio recording is enabled for a
" - + "channel is to record all aliases except any alias with a
" - + "non-recordable identifier"; - - public NonRecordableEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) ); - - add( new JLabel( "Non-Recordable" ), "wrap" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( NonRecordableEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - @Override - public void save() - { - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/record/Record.java b/src/main/java/io/github/dsheirer/alias/id/record/Record.java new file mode 100644 index 00000000..85ab55ca --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/record/Record.java @@ -0,0 +1,56 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.alias.id.record; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; + +/** + * Alias identifier to flag an alias for audio recording + */ +public class Record extends AliasID +{ + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @Override + public AliasIDType getType() + { + return AliasIDType.RECORD; + } + + @Override + public boolean isValid() + { + return true; + } + + @Override + public boolean matches(AliasID id) + { + return false; + } + + @Override + public String toString() + { + return "Record Audio"; + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java b/src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java new file mode 100644 index 00000000..91b8c809 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/record/RecordEditor.java @@ -0,0 +1,73 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.record; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.gui.editor.DocumentListenerEditor; +import net.miginfocom.swing.MigLayout; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class RecordEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "" + + "

Record Audio

" + + "Any audio associated with this alias will be recorded.

"; + + public RecordEditor(AliasID aliasID ) + { + initGUI(); + + setItem( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) ); + + add( new JLabel( "Record Audio" ), "wrap" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( RecordEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + @Override + public void save() + { + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/siteID/SiteID.java b/src/main/java/io/github/dsheirer/alias/id/siteID/SiteID.java index acf3016e..f4bdbca8 100644 --- a/src/main/java/io/github/dsheirer/alias/id/siteID/SiteID.java +++ b/src/main/java/io/github/dsheirer/alias/id/siteID/SiteID.java @@ -48,7 +48,7 @@ public class SiteID extends AliasID public String toString() { - return "Site: " + mSite; + return "Site: " + (mSite != null ? mSite : "(empty)") + (isValid() ? "" : " **NOT VALID**"); } @Override diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java new file mode 100644 index 00000000..7562ef84 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/Talkgroup.java @@ -0,0 +1,107 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.talkgroup; + + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; +import io.github.dsheirer.protocol.Protocol; + +/** + * Integer talkgroup identifier with protocol. + */ +public class Talkgroup extends AliasID +{ + private Protocol mProtocol; + private int mValue; + + public Talkgroup() + { + //No arg JAXB constructor + } + + public Talkgroup(Protocol protocol, int value) + { + mProtocol = protocol; + mValue = value; + } + + @JacksonXmlProperty(isAttribute = true, localName = "value") + public int getValue() + { + return mValue; + } + + public void setValue(int value) + { + mValue = value; + } + + @JacksonXmlProperty(isAttribute = true, localName = "protocol") + public Protocol getProtocol() + { + return mProtocol; + } + + public void setProtocol(Protocol protocol) + { + mProtocol = protocol; + } + + @Override + public boolean isValid() + { + return mValue != 0; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("Talkgroup:").append(mValue).append(" Protocol:").append((mProtocol != null ? mProtocol : "(unspecified)")); + + if(!isValid()) + { + sb.append(" **NOT VALID**"); + } + + return sb.toString(); + } + + @Override + public boolean matches(AliasID id) + { + if(id instanceof Talkgroup) + { + Talkgroup tgid = (Talkgroup)id; + return (getProtocol() == tgid.getProtocol()) && (getValue() == tgid.getValue()); + } + + return false; + } + + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @Override + public AliasIDType getType() + { + return AliasIDType.TALKGROUP; + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java new file mode 100644 index 00000000..68132da5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupEditor.java @@ -0,0 +1,164 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.talkgroup; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.gui.editor.DocumentListenerEditor; +import io.github.dsheirer.protocol.Protocol; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class TalkgroupEditor extends DocumentListenerEditor +{ + private final static Logger mLog = LoggerFactory.getLogger(TalkgroupEditor.class); + + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = + "

Talkgroup Identifier

"; + + private JComboBox mComboProtocol; + private JFormattedTextField mTalkgroupField; + + public TalkgroupEditor(AliasID aliasID) + { + initGUI(); + setItem(aliasID); + } + + private void initGUI() + { + setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); + + add(new JLabel("Protocol:")); + + mComboProtocol = new JComboBox(); + + DefaultComboBoxModel model = new DefaultComboBoxModel<>(); + + for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS) + { + model.addElement(protocol); + } + + mComboProtocol.setModel(model); + mComboProtocol.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + Protocol selected = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); + setModified(true); +// ValidatingEditor editor = DecoderFactory.getEditor(selected, mChannelMapModel); +// setEditor(editor); + } + }); + + add(mComboProtocol); + + add(new JLabel("Value:")); + + mTalkgroupField = new JFormattedTextField(); + mTalkgroupField.setColumns(10); + mTalkgroupField.setValue(new Integer(0)); + mTalkgroupField.getDocument().addDocumentListener(this); + mTalkgroupField.setToolTipText(HELP_TEXT); + add(mTalkgroupField, "growx,push"); + + JLabel help = new JLabel("Help ..."); + help.setForeground(Color.BLUE.brighter()); + help.setCursor(new Cursor(Cursor.HAND_CURSOR)); + help.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + JOptionPane.showMessageDialog(TalkgroupEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); + } + }); + add(help, "align left"); + } + + public Talkgroup getTalkgroup() + { + if(getItem() instanceof Talkgroup) + { + return (Talkgroup)getItem(); + } + + return null; + } + + @Override + public void setItem(AliasID aliasID) + { + super.setItem(aliasID); + + Talkgroup talkgroup = getTalkgroup(); + + if(talkgroup != null) + { + mComboProtocol.setSelectedItem(talkgroup.getProtocol()); + mTalkgroupField.setValue(new Integer(talkgroup.getValue())); + } + + setModified(false); + + repaint(); + } + + @Override + public void save() + { + Talkgroup talkgroup = getTalkgroup(); + + if(talkgroup != null) + { + Protocol selected = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); + talkgroup.setProtocol(selected); + + try + { + int value = ((Number)mTalkgroupField.getValue()).intValue(); + talkgroup.setValue(value); + } + catch(Exception e) + { + mLog.error("Error parsing minimum talkgroup value from [" + mTalkgroupField.getValue() + "]"); + } + } + + setModified(false); + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupIDEditor.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupIDEditor.java deleted file mode 100644 index 8cf061c5..00000000 --- a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupIDEditor.java +++ /dev/null @@ -1,116 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.alias.id.talkgroup; - -import io.github.dsheirer.alias.id.AliasID; -import io.github.dsheirer.gui.editor.DocumentListenerEditor; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -public class TalkgroupIDEditor extends DocumentListenerEditor -{ - private static final long serialVersionUID = 1L; - - private static final String HELP_TEXT = - "

Talkgroup Identifier

" - + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)
" - + "LTR: A-HH-TTT where A=Area H=Home T=Talkgroup (0-01-128)
" - + "Passport: 5-digit number (12345 or 00023)
" - + "
" - + "Wildcard: use an asterisk (*) in place of each talkgroup digit (e.g. 0*1*5)" - + ""; - - private JTextField mTextField; - - public TalkgroupIDEditor( AliasID aliasID ) - { - initGUI(); - - setItem( aliasID ); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - - add( new JLabel( "Talkgroup:" ) ); - - mTextField = new JTextField(); - mTextField.getDocument().addDocumentListener( this ); - mTextField.setToolTipText( HELP_TEXT ); - add( mTextField, "growx,push" ); - - JLabel help = new JLabel( "Help ..." ); - help.setForeground( Color.BLUE.brighter() ); - help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - help.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - JOptionPane.showMessageDialog( TalkgroupIDEditor.this, - HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( help, "align left" ); - } - - public TalkgroupID getTalkgroupID() - { - if( getItem() instanceof TalkgroupID ) - { - return (TalkgroupID)getItem(); - } - - return null; - } - - @Override - public void setItem( AliasID aliasID ) - { - super.setItem( aliasID ); - - TalkgroupID talkgroup = getTalkgroupID(); - - if( talkgroup != null ) - { - mTextField.setText( talkgroup.getTalkgroup() ); - } - - setModified( false ); - - repaint(); - } - - @Override - public void save() - { - TalkgroupID talkgroup = getTalkgroupID(); - - if( talkgroup != null ) - { - talkgroup.setTalkgroup( mTextField.getText() ); - } - - setModified( false ); - } -} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java new file mode 100644 index 00000000..ba0f92cd --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRange.java @@ -0,0 +1,129 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.talkgroup; + + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; +import io.github.dsheirer.protocol.Protocol; + +/** + * Integer talkgroup identifier range of values with protocol. + */ +public class TalkgroupRange extends AliasID +{ + private Protocol mProtocol; + private int mMinTalkgroup; + private int mMaxTalkgroup; + + public TalkgroupRange() + { + //No arg JAXB constructor + } + + /** + * Creates a talkgroup range of from - to talkgroup values (inclusive) for the specified protocol + * @param protocol for the talkgroup range + * @param minTalkgroup starting or minimum talkgroup value + * @param maxTalkgroup ending or maximum talkgroup value + */ + public TalkgroupRange(Protocol protocol, int minTalkgroup, int maxTalkgroup) + { + mProtocol = protocol; + mMinTalkgroup = minTalkgroup; + mMaxTalkgroup = maxTalkgroup; + } + + @JacksonXmlProperty(isAttribute = true, localName = "min") + public int getMinTalkgroup() + { + return mMinTalkgroup; + } + + public void setMinTalkgroup(int minTalkgroup) + { + mMinTalkgroup = minTalkgroup; + } + + @JacksonXmlProperty(isAttribute = true, localName = "max") + public int getMaxTalkgroup() + { + return mMaxTalkgroup; + } + + public void setMaxTalkgroup(int maxTalkgroup) + { + mMaxTalkgroup = maxTalkgroup; + } + + @JacksonXmlProperty(isAttribute = true, localName = "protocol") + public Protocol getProtocol() + { + return mProtocol; + } + + public void setProtocol(Protocol protocol) + { + mProtocol = protocol; + } + + @Override + public boolean isValid() + { + return mProtocol != null && mMinTalkgroup != 0 && mMaxTalkgroup != 0 && mMinTalkgroup <= mMaxTalkgroup; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("Talkgroup Range:").append(mMinTalkgroup).append(" to ").append(mMaxTalkgroup) + .append(" Protocol:").append(mProtocol == null ? "unspecified" : mProtocol); + + if(!isValid()) + { + sb.append(" **NOT VALID**"); + } + + return sb.toString(); + } + + @Override + public boolean matches(AliasID id) + { + if(id instanceof TalkgroupRange) + { + TalkgroupRange tgid = (TalkgroupRange)id; + return (getProtocol() == tgid.getProtocol()) && + (getMinTalkgroup() == tgid.getMinTalkgroup()) && + (getMaxTalkgroup() == tgid.getMaxTalkgroup()); + } + + return false; + } + + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @Override + public AliasIDType getType() + { + return AliasIDType.TALKGROUP_RANGE; + } +} diff --git a/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java new file mode 100644 index 00000000..0f1c613b --- /dev/null +++ b/src/main/java/io/github/dsheirer/alias/id/talkgroup/TalkgroupRangeEditor.java @@ -0,0 +1,185 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.alias.id.talkgroup; + +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.gui.editor.DocumentListenerEditor; +import io.github.dsheirer.protocol.Protocol; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class TalkgroupRangeEditor extends DocumentListenerEditor +{ + private final static Logger mLog = LoggerFactory.getLogger(TalkgroupRangeEditor.class); + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = + "

Talkgroup Range Identifier

Identifies a range of talkgroup values"; + + private JComboBox mComboProtocol; + private JFormattedTextField mMinTalkgroupField; + private JFormattedTextField mMaxTalkgroupField; + + public TalkgroupRangeEditor(AliasID aliasID) + { + initGUI(); + + setItem(aliasID); + } + + private void initGUI() + { + setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]")); + + add(new JLabel("Protocol:")); + + mComboProtocol = new JComboBox(); + + DefaultComboBoxModel model = new DefaultComboBoxModel<>(); + + for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS) + { + model.addElement(protocol); + } + + mComboProtocol.setModel(model); + mComboProtocol.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + Protocol selected = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); + setModified(true); +// ValidatingEditor editor = DecoderFactory.getEditor(selected, mChannelMapModel); +// setEditor(editor); + } + }); + + add(mComboProtocol); + + add(new JLabel("Min:")); + + mMinTalkgroupField = new JFormattedTextField(); + mMinTalkgroupField.setColumns(10); + mMinTalkgroupField.setValue(new Integer(0)); + mMinTalkgroupField.getDocument().addDocumentListener(this); + mMinTalkgroupField.setToolTipText(HELP_TEXT); + add(mMinTalkgroupField, "growx,push"); + + add(new JLabel("Max:")); + + mMaxTalkgroupField = new JFormattedTextField(); + mMaxTalkgroupField.setColumns(10); + mMaxTalkgroupField.setValue(new Integer(0)); + mMaxTalkgroupField.getDocument().addDocumentListener(this); + mMaxTalkgroupField.setToolTipText(HELP_TEXT); + add(mMaxTalkgroupField, "growx,push"); + + JLabel help = new JLabel("Help ..."); + help.setForeground(Color.BLUE.brighter()); + help.setCursor(new Cursor(Cursor.HAND_CURSOR)); + help.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + JOptionPane.showMessageDialog(TalkgroupRangeEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE); + } + }); + add(help, "align left"); + } + + public TalkgroupRange getTalkgroupRange() + { + if(getItem() instanceof TalkgroupRange) + { + return (TalkgroupRange)getItem(); + } + + return null; + } + + @Override + public void setItem(AliasID aliasID) + { + super.setItem(aliasID); + + TalkgroupRange talkgroupRange = getTalkgroupRange(); + + if(talkgroupRange != null) + { + mComboProtocol.setSelectedItem(talkgroupRange.getProtocol()); + mMinTalkgroupField.setValue(new Integer(talkgroupRange.getMinTalkgroup())); + mMaxTalkgroupField.setValue(new Integer(talkgroupRange.getMaxTalkgroup())); + } + + setModified(false); + + repaint(); + } + + @Override + public void save() + { + TalkgroupRange talkgroupRange = getTalkgroupRange(); + + if(talkgroupRange != null) + { + Protocol selected = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex()); + talkgroupRange.setProtocol(selected); + + try + { + int minTalkgroup = ((Number)mMinTalkgroupField.getValue()).intValue(); + talkgroupRange.setMinTalkgroup(minTalkgroup); + } + catch(Exception e) + { + mLog.error("Error parsing minimum talkgroup value from [" + mMinTalkgroupField.getText() + "]"); + } + + try + { + int maxTalkgroup = ((Number)mMaxTalkgroupField.getValue()).intValue(); + talkgroupRange.setMaxTalkgroup(maxTalkgroup); + } + catch(Exception e) + { + mLog.error("Error parsing maximum talkgroup value from [" + mMaxTalkgroupField.getText() + "]"); + } + } + + setModified(false); + } +} diff --git a/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java b/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java new file mode 100644 index 00000000..b243dc70 --- /dev/null +++ b/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java @@ -0,0 +1,111 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.audio; + +import io.github.dsheirer.audio.squelch.ISquelchStateListener; +import io.github.dsheirer.identifier.IdentifierUpdateListener; +import io.github.dsheirer.identifier.IdentifierUpdateNotification; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.module.Module; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.buffer.ReusableAudioPacket; + +/** + * Base audio module implementation. + */ +public abstract class AbstractAudioModule extends Module implements IAudioPacketProvider, IdentifierUpdateListener, + ISquelchStateListener +{ + //Static unique audio channel identifier. + private static int AUDIO_CHANNEL_ID_GENERATOR = 1; + + private int mAudioChannelId = AUDIO_CHANNEL_ID_GENERATOR++; + private Listener mAudioPacketListener; + private MutableIdentifierCollection mIdentifierCollection = new MutableIdentifierCollection(); + + /** + * Constructs an abstract audio module + */ + public AbstractAudioModule() + { + } + + /** + * Unique channel identifier for use in tagging audio packets with an audio channel ID so that they can + * be identified within a combined audio packet stream + */ + public int getAudioChannelId() + { + return mAudioChannelId; + } + + /** + * Receive updated identifiers from decoder state(s). + */ + @Override + public Listener getIdentifierUpdateListener() + { + return mIdentifierCollection; + } + + /** + * Identifier collection containing the current set of identifiers received from the decoder state(s). + */ + public MutableIdentifierCollection getIdentifierCollection() + { + return mIdentifierCollection; + } + + /** + * Registers an audio packet listener to receive the output from this audio module. + */ + @Override + public void setAudioPacketListener(Listener listener) + { + mAudioPacketListener = listener; + } + + /** + * Unregisters the audio packet listener from receiving audio packets from this module. + */ + @Override + public void removeAudioPacketListener() + { + mAudioPacketListener = null; + } + + /** + * Registered listener for receiving audio packets produced by this module + */ + public Listener getAudioPacketListener() + { + return mAudioPacketListener; + } + + /** + * Indicates if there is a listener registered to receive audio packets from this audio module. + */ + public boolean hasAudioPacketListener() + { + return mAudioPacketListener != null; + } + +} diff --git a/src/main/java/io/github/dsheirer/audio/AudioMetadataProcessor.java b/src/main/java/io/github/dsheirer/audio/AudioMetadataProcessor.java new file mode 100644 index 00000000..4541195e --- /dev/null +++ b/src/main/java/io/github/dsheirer/audio/AudioMetadataProcessor.java @@ -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 + * ***************************************************************************** + */ + +package io.github.dsheirer.audio; + +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.sample.buffer.ReusableAudioPacket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Processes audio packets to assign attributes to each packet based on alias list settings. Updates the + * monitoring, recording and streaming attributes. + */ +public class AudioMetadataProcessor +{ + private static final Logger mLog = LoggerFactory.getLogger(AudioMetadataProcessor.class); + private AliasModel mAliasModel; + private Map mAliasListMap = new HashMap<>(); + private Set mAliasListRetrievedSet = new TreeSet<>(); + + public AudioMetadataProcessor(AliasModel aliasModel) + { + mAliasModel = aliasModel; + } + + public void process(ReusableAudioPacket audioPacket) + { + if(audioPacket.hasIdentifierCollection() && audioPacket.getIdentifierCollection().hasAliasListConfiguration()) + { + AliasList aliasList = null; + + if(mAliasListRetrievedSet.contains(audioPacket.getAudioChannelId())) + { + aliasList = mAliasListMap.get(audioPacket.getAudioChannelId()); + } + else + { + aliasList = mAliasModel.getAliasList(audioPacket.getIdentifierCollection().getAliasListConfiguration()); + + if(aliasList != null) + { + mAliasListMap.put(audioPacket.getAudioChannelId(), aliasList); + mAliasListRetrievedSet.add(audioPacket.getAudioChannelId()); + } + } + + if(aliasList != null) + { + audioPacket.addBroadcastChannels(aliasList.getBroadcastChannels(audioPacket.getIdentifierCollection())); + audioPacket.setRecordable(aliasList.isRecordable(audioPacket.getIdentifierCollection())); + audioPacket.setMonitoringPriority(aliasList.getAudioPlaybackPriority(audioPacket.getIdentifierCollection())); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/audio/AudioModule.java b/src/main/java/io/github/dsheirer/audio/AudioModule.java index 5e60c0ca..ceb0efe1 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioModule.java +++ b/src/main/java/io/github/dsheirer/audio/AudioModule.java @@ -15,14 +15,11 @@ ******************************************************************************/ package io.github.dsheirer.audio; -import io.github.dsheirer.audio.squelch.ISquelchStateListener; import io.github.dsheirer.audio.squelch.SquelchState; -import io.github.dsheirer.channel.metadata.Metadata; import io.github.dsheirer.dsp.filter.design.FilterDesignException; import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter2; import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner; -import io.github.dsheirer.module.Module; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.IReusableBufferListener; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; @@ -39,12 +36,10 @@ import org.slf4j.LoggerFactory; * * This class is designed to support 8 kHz sample rate demodulated audio. */ -public class AudioModule extends Module implements IAudioPacketProvider, IReusableBufferListener, - ISquelchStateListener, Listener +public class AudioModule extends AbstractAudioModule implements IReusableBufferListener, Listener { - protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class); - private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("AudioModule"); + private static final Logger mLog = LoggerFactory.getLogger(AudioModule.class); private static float[] sHighPassFilterCoefficients; static @@ -73,32 +68,29 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab } } + private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("AudioModule"); private RealFIRFilter2 mHighPassFilter = new RealFIRFilter2(sHighPassFilterCoefficients); private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private SquelchState mSquelchState = SquelchState.SQUELCH; - private Listener mAudioPacketListener; - private Metadata mMetadata; /** * Creates an Audio Module. - * - * @param metadata to use for audio packets produced by this audio module. */ - public AudioModule(Metadata metadata) + public AudioModule() { - mMetadata = metadata; } @Override public void dispose() { - mAudioPacketListener = null; + removeAudioPacketListener(); mSquelchStateListener = null; } @Override public void reset() { + getIdentifierCollection().clear(); } @Override @@ -111,26 +103,17 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab public void stop() { /* Issue an end-audio packet in case a recorder is still rolling */ - if(mAudioPacketListener != null) + if(hasAudioPacketListener()) { ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer(); - endAudioPacket.setMetadata(mMetadata.copyOf()); + endAudioPacket.resetAttributes(); + endAudioPacket.setAudioChannelId(getAudioChannelId()); + endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf()); endAudioPacket.incrementUserCount(); - mAudioPacketListener.receive(endAudioPacket); + getAudioPacketListener().receive(endAudioPacket); } } - @Override - public void setAudioPacketListener(Listener listener) - { - mAudioPacketListener = listener; - } - - @Override - public void removeAudioPacketListener() - { - mAudioPacketListener = null; - } @Override public Listener getSquelchStateListener() @@ -141,15 +124,17 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab @Override public void receive(ReusableFloatBuffer reusableFloatBuffer) { - if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH) + if(hasAudioPacketListener() && mSquelchState == SquelchState.UNSQUELCH) { ReusableFloatBuffer highPassFiltered = mHighPassFilter.filter(reusableFloatBuffer); ReusableAudioPacket audioPacket = mAudioPacketQueue.getBuffer(highPassFiltered.getSampleCount()); + audioPacket.resetAttributes(); + audioPacket.setAudioChannelId(getAudioChannelId()); audioPacket.loadAudioFrom(highPassFiltered); - audioPacket.setMetadata(mMetadata.copyOf()); + audioPacket.setIdentifierCollection(getIdentifierCollection().copyOf()); - mAudioPacketListener.receive(audioPacket); + getAudioPacketListener().receive(audioPacket); highPassFiltered.decrementUserCount(); } @@ -174,11 +159,13 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab @Override public void receive(SquelchState state) { - if(state == SquelchState.SQUELCH && mAudioPacketListener != null) + if(state == SquelchState.SQUELCH && hasAudioPacketListener()) { ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer(); - endAudioPacket.setMetadata(mMetadata.copyOf()); - mAudioPacketListener.receive(endAudioPacket); + endAudioPacket.resetAttributes(); + endAudioPacket.setAudioChannelId(getAudioChannelId()); + endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf()); + getAudioPacketListener().receive(endAudioPacket); } mSquelchState = state; diff --git a/src/main/java/io/github/dsheirer/audio/AudioPacketManager.java b/src/main/java/io/github/dsheirer/audio/AudioPacketManager.java new file mode 100644 index 00000000..e6c0ae36 --- /dev/null +++ b/src/main/java/io/github/dsheirer/audio/AudioPacketManager.java @@ -0,0 +1,98 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.audio; + +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.dsp.filter.channelizer.ContinuousReusableBufferProcessor; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.buffer.ReusableAudioPacket; +import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Processes audio packets from all decoder channels, assigns alias attributes and distributes packets + * for playback, streaming and recording. + */ +public class AudioPacketManager implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger(AudioPacketManager.class); + private AudioMetadataProcessor mAudioMetadataProcessor; + private ReusableBufferBroadcaster mBroadcaster = new ReusableBufferBroadcaster<>(); + private ContinuousReusableBufferProcessor mBufferProcessor; + + public AudioPacketManager(AliasModel aliasModel) + { + mAudioMetadataProcessor = new AudioMetadataProcessor(aliasModel); + mBufferProcessor = new ContinuousReusableBufferProcessor<>(10000, 500); + mBufferProcessor.setListener(new Listener>() + { + @Override + public void receive(List reusableAudioPackets) + { + for(ReusableAudioPacket audioPacket: reusableAudioPackets) + { + mAudioMetadataProcessor.process(audioPacket); + mBroadcaster.broadcast(audioPacket); + } + } + }); + } + + public void start() + { + mLog.info("Audio packet manager started"); + mBufferProcessor.start(); + } + + public void stop() + { + mLog.info("Audio packet manager stopped"); + mBufferProcessor.stop(); + } + + /** + * Primary processing method for receiving audio packets from all processing channels. + */ + @Override + public void receive(ReusableAudioPacket audioPacket) + { + mBufferProcessor.receive(audioPacket); + } + + /** + * Adds the listener to receive enriched audio packets from this manager + */ + public void addListener(Listener listener) + { + mBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving enriched audio packets from this manager + */ + public void removeListener(Listener listener) + { + mBroadcaster.removeListener(listener); + } +} diff --git a/src/main/java/io/github/dsheirer/audio/IAudioController.java b/src/main/java/io/github/dsheirer/audio/IAudioController.java index a6fe007b..e374b3ce 100644 --- a/src/main/java/io/github/dsheirer/audio/IAudioController.java +++ b/src/main/java/io/github/dsheirer/audio/IAudioController.java @@ -1,6 +1,6 @@ package io.github.dsheirer.audio; -import io.github.dsheirer.audio.output.AudioOutput; +import io.github.dsheirer.audio.playback.AudioOutput; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.mixer.MixerChannelConfiguration; diff --git a/src/main/java/io/github/dsheirer/audio/InversionFrequency.java b/src/main/java/io/github/dsheirer/audio/InversionFrequency.java deleted file mode 100644 index 071dc8bd..00000000 --- a/src/main/java/io/github/dsheirer/audio/InversionFrequency.java +++ /dev/null @@ -1,59 +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 - ******************************************************************************/ -package io.github.dsheirer.audio; - -/** - * Standard voice inversion foldover frequencies - * - * Reference: http://en.wikipedia.org/wiki/Voice_inversion - */ -public enum InversionFrequency -{ - HZ_2500( 2500 ), - HZ_2550( 2550 ), - HZ_2600( 2600 ), - HZ_2632( 2632 ), - HZ_2675( 2675 ), - HZ_2718( 2718 ), - HZ_2800( 2800 ), - HZ_2868( 2868 ), - HZ_2950( 2950 ), - HZ_3023( 3023 ), - HZ_3075( 3075 ), - HZ_3107( 3107 ), - HZ_3150( 3150 ), - HZ_3196( 3196 ), - HZ_3333( 3333 ), - HZ_3339( 3339 ), - HZ_3400( 3400 ), - HZ_3496( 3496 ), - HZ_3729( 3729 ), - HZ_4096( 4096 ); - - private int mFrequency; - - private InversionFrequency( int frequency ) - { - mFrequency = frequency; - } - - public int getFrequency() - { - return mFrequency; - } -} diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java index 876e2ea9..2c48963f 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/AudioBroadcaster.java @@ -19,7 +19,7 @@ package io.github.dsheirer.audio.broadcast; import io.github.dsheirer.audio.convert.ISilenceGenerator; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; @@ -99,15 +99,15 @@ public abstract class AudioBroadcaster implements Listener /** * Broadcasts the next song's audio metadata prior to streaming the next song. * - * @param metadata for the next recording that will be streamed + * @param identifierCollection for the next recording that will be streamed */ - protected void broadcastMetadata(Metadata metadata) + protected void broadcastMetadata(IdentifierCollection identifierCollection) { IBroadcastMetadataUpdater metadataUpdater = getMetadataUpdater(); if(metadataUpdater != null) { - metadataUpdater.update(metadata); + metadataUpdater.update(identifierCollection); } } @@ -442,7 +442,7 @@ public abstract class AudioBroadcaster implements Listener if(connected()) { - broadcastMetadata(nextRecording.getMetadata()); + broadcastMetadata(nextRecording.getIdentifierCollection()); } metadataUpdateRequired = false; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/AudioRecording.java b/src/main/java/io/github/dsheirer/audio/broadcast/AudioRecording.java index 5edbe272..aa672063 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/AudioRecording.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/AudioRecording.java @@ -18,7 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.IdentifierCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,20 +33,20 @@ public class AudioRecording implements Comparable private long mStartTime; private long mRecordingLength; private AtomicInteger mPendingReplayCount = new AtomicInteger(); - private Metadata mMetadata; + private IdentifierCollection mIdentifierCollection; /** * Audio recording that is ready to be streamed * * @param path to the audio recording file - * @param metadata associated with the recording + * @param identifierCollection associated with the recording * @param start time of recording in milliseconds since epoch * @param recordingLength in milliseconds */ - public AudioRecording(Path path, Metadata metadata, long start, long recordingLength) + public AudioRecording(Path path, IdentifierCollection identifierCollection, long start, long recordingLength) { mPath = path; - mMetadata = metadata; + mIdentifierCollection = identifierCollection; mStartTime = start; mRecordingLength = recordingLength; } @@ -60,11 +60,19 @@ public class AudioRecording implements Comparable } /** - * Optional audio metadata for the recording. + * Optional audio metadata/identifiers for the recording. */ - public Metadata getMetadata() + public IdentifierCollection getIdentifierCollection() { - return mMetadata; + return mIdentifierCollection; + } + + /** + * Indicates if this recording contains an optional identifier collection. + */ + public boolean hasIdentifierCollection() + { + return mIdentifierCollection != null; } /** diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java index 85f6e43c..bacd227b 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastFactory.java @@ -60,7 +60,7 @@ public class BroadcastFactory * @param configuration describing the server and audio types * @return configured broadcaster or null */ - public static AudioBroadcaster getBroadcaster(BroadcastConfiguration configuration) + public static AudioBroadcaster getBroadcaster(BroadcastConfiguration configuration, AliasModel aliasModel) { if(configuration != null) { @@ -71,15 +71,15 @@ public class BroadcastFactory switch(configuration.getBroadcastServerType()) { case BROADCASTIFY: - return new IcecastTCPAudioBroadcaster((BroadcastifyConfiguration) configuration); + return new IcecastTCPAudioBroadcaster((BroadcastifyConfiguration) configuration, aliasModel); case ICECAST_TCP: - return new IcecastTCPAudioBroadcaster((IcecastTCPConfiguration) configuration); + return new IcecastTCPAudioBroadcaster((IcecastTCPConfiguration) configuration, aliasModel); case ICECAST_HTTP: - return new IcecastHTTPAudioBroadcaster((IcecastHTTPConfiguration) configuration); + return new IcecastHTTPAudioBroadcaster((IcecastHTTPConfiguration) configuration, aliasModel); case SHOUTCAST_V1: - return new ShoutcastV1AudioBroadcaster((ShoutcastV1Configuration) configuration); + return new ShoutcastV1AudioBroadcaster((ShoutcastV1Configuration) configuration, aliasModel); case SHOUTCAST_V2: - return new ShoutcastV2AudioBroadcaster((ShoutcastV2Configuration) configuration); + return new ShoutcastV2AudioBroadcaster((ShoutcastV2Configuration) configuration, aliasModel); case UNKNOWN: default: mLog.info("Unrecognized broadcastAudio configuration: " + configuration.getBroadcastFormat().name()); diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java index 986306fd..30d482e2 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java @@ -18,8 +18,9 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; -import io.github.dsheirer.channel.metadata.Metadata; import io.github.dsheirer.icon.IconManager; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.sample.Broadcaster; @@ -72,13 +73,15 @@ public class BroadcastModel extends AbstractTableModel implements Listener mBroadcasterMap = new HashMap<>(); private IconManager mIconManager; private StreamManager mStreamManager; + private AliasModel mAliasModel; private Broadcaster mBroadcastEventBroadcaster = new Broadcaster<>(); /** * Model for managing Broadcast configurations and any associated broadcaster instances. */ - public BroadcastModel(IconManager iconManager) + public BroadcastModel(AliasModel aliasModel, IconManager iconManager) { + mAliasModel = aliasModel; mIconManager = iconManager; mStreamManager = new StreamManager(new CompletedRecordingListener(), BroadcastFormat.MP3, SystemProperties.getInstance().getApplicationFolder(TEMPORARY_STREAM_DIRECTORY)); @@ -278,9 +281,9 @@ public class BroadcastModel extends AbstractTableModel implements Listener @Override public void receive(ReusableAudioPacket audioPacket) { - if(mRunning.get() && audioPacket.hasMetadata()) + if(mRunning.get() && audioPacket.hasIdentifierCollection()) { synchronized(mStreamRecorders) { - int channelMetadataID = audioPacket.getMetadata().getMetadataID(); + int channelMetadataID = audioPacket.getAudioChannelId(); ReusableAudioPacket.Type type = audioPacket.getType(); @@ -139,8 +139,7 @@ public class StreamManager implements Listener } else { - AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(), - mBroadcastFormat); + AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(), mBroadcastFormat); recorder.start(); audioPacket.incrementUserCount(); recorder.receive(audioPacket); @@ -181,7 +180,7 @@ public class StreamManager implements Listener public void receive(AudioRecorder audioRecorder) { AudioRecording audioRecording = - new AudioRecording(audioRecorder.getPath(), audioRecorder.getMetadata(), + new AudioRecording(audioRecorder.getPath(), audioRecorder.getIdentifierCollection(), audioRecorder.getTimeRecordingStart(), audioRecorder.getRecordingLength()); if(mAudioRecordingListener != null) diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastAudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastAudioBroadcaster.java index 87bc5dae..3ce48058 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastAudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastAudioBroadcaster.java @@ -18,6 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.icecast; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.AudioBroadcaster; import io.github.dsheirer.audio.broadcast.BroadcastConfiguration; import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; @@ -25,10 +26,12 @@ import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; public abstract class IcecastAudioBroadcaster extends AudioBroadcaster { private IBroadcastMetadataUpdater mMetadataUpdater; + private AliasModel mAliasModel; - public IcecastAudioBroadcaster(BroadcastConfiguration broadcastConfiguration) + public IcecastAudioBroadcaster(BroadcastConfiguration broadcastConfiguration, AliasModel aliasModel) { super(broadcastConfiguration); + mAliasModel = aliasModel; } /** @@ -44,7 +47,7 @@ public abstract class IcecastAudioBroadcaster extends AudioBroadcaster { if(mMetadataUpdater == null) { - mMetadataUpdater = new IcecastBroadcastMetadataUpdater(getConfiguration()); + mMetadataUpdater = new IcecastBroadcastMetadataUpdater(getConfiguration(), mAliasModel); } return mMetadataUpdater; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java index 924fd8ec..3c084c2b 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastBroadcastMetadataUpdater.java @@ -18,8 +18,15 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.icecast; +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.util.ThreadPool; import org.apache.mina.core.RuntimeIoException; @@ -52,7 +59,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate private final static String UTF8 = "UTF-8"; private IcecastConfiguration mIcecastConfiguration; - + private AliasModel mAliasModel; private NioSocketConnector mSocketConnector; private AtomicBoolean mUpdating = new AtomicBoolean(); private Queue mMetadataQueue = new LinkedTransferQueue<>(); @@ -65,9 +72,10 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate * broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update * sequence, those updates will be queued and processed in the order received. */ - public IcecastBroadcastMetadataUpdater(IcecastConfiguration icecastConfiguration) + public IcecastBroadcastMetadataUpdater(IcecastConfiguration icecastConfiguration, AliasModel aliasModel) { mIcecastConfiguration = icecastConfiguration; + mAliasModel = aliasModel; } /** @@ -113,9 +121,9 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate * and will remain in the update queue until the next metadata update is requested. However, this is a design * trade-off to avoid having a scheduled runnable repeatedly processing the update queue. */ - public void update(Metadata metadata) + public void update(IdentifierCollection identifierCollection) { - mMetadataQueue.offer(getSong(metadata)); + mMetadataQueue.offer(getSong(identifierCollection)); if(!mUpdating.get() && !mMetadataQueue.isEmpty()) { @@ -181,45 +189,54 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate /** * Creates the song information for a metadata update */ - private static String getSong(Metadata metadata) + private String getSong(IdentifierCollection identifierCollection) { StringBuilder sb = new StringBuilder(); - if(metadata != null) + if(identifierCollection != null) { - String to = metadata.getPrimaryAddressTo().getIdentifier(); + AliasList aliasList = mAliasModel.getAliasList(identifierCollection); - sb.append("TO:"); + Identifier to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.PATCH_GROUP, Role.TO); - if(metadata.getPrimaryAddressTo().hasAlias()) + if(to == null) { - sb.append(metadata.getPrimaryAddressTo().getAlias().getName()); + to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO); } - else if(metadata.getPrimaryAddressTo().hasIdentifier()) + + if(to != null) { - sb.append(metadata.getPrimaryAddressTo().getIdentifier()); + sb.append("TO:").append(to); + + Alias toAlias = aliasList != null ? aliasList.getAlias(to) : null; + + if(toAlias != null) + { + sb.append(" ").append(toAlias.getName()); + } } else { - sb.append("UNKNOWN"); + sb.append("TO:UNKNOWN"); } + Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM); - sb.append(" FROM:"); + if(from != null) + { + sb.append(" FROM:").append(from); - if(metadata.getPrimaryAddressFrom().hasAlias()) - { - sb.append(metadata.getPrimaryAddressFrom().getAlias().getName()); - } - else if(metadata.getPrimaryAddressFrom().hasIdentifier()) - { - sb.append(metadata.getPrimaryAddressFrom().getIdentifier()); + Alias fromAlias = aliasList != null ? aliasList.getAlias(from) : null; + + if(fromAlias != null) + { + sb.append(" ").append(fromAlias.getName()); + } } else { - sb.append("UNKNOWN"); + sb.append(" FROM:UNKNOWN"); } - } else { diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastHTTPAudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastHTTPAudioBroadcaster.java index 3655dc20..2f72a8f6 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastHTTPAudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastHTTPAudioBroadcaster.java @@ -18,6 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.icecast; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.BroadcastState; import io.github.dsheirer.audio.convert.MP3AudioConverter; import io.github.dsheirer.properties.SystemProperties; @@ -69,9 +70,9 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster * * @param configuration for the Icecast stream */ - public IcecastHTTPAudioBroadcaster(IcecastHTTPConfiguration configuration) + public IcecastHTTPAudioBroadcaster(IcecastHTTPConfiguration configuration, AliasModel aliasModel) { - super(configuration); + super(configuration, aliasModel); } /** diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java index 8618d6b2..fe496762 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java @@ -18,6 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.icecast; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.BroadcastState; import io.github.dsheirer.audio.broadcast.icecast.codec.IcecastCodecFactory; import io.github.dsheirer.audio.convert.MP3AudioConverter; @@ -65,9 +66,9 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster * * @param configuration for the Icecast stream */ - public IcecastTCPAudioBroadcaster(IcecastTCPConfiguration configuration) + public IcecastTCPAudioBroadcaster(IcecastTCPConfiguration configuration, AliasModel aliasModel) { - super(configuration); + super(configuration, aliasModel); } /** diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1AudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1AudioBroadcaster.java index 7dea307a..e3d3f0c9 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1AudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1AudioBroadcaster.java @@ -18,6 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.shoutcast.v1; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.AudioBroadcaster; import io.github.dsheirer.audio.broadcast.BroadcastState; import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; @@ -47,7 +48,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster private NioSocketConnector mSocketConnector; private IoSession mStreamingSession = null; private IBroadcastMetadataUpdater mMetadataUpdater; - + private AliasModel mAliasModel; private long mLastConnectionAttempt = 0; private AtomicBoolean mConnecting = new AtomicBoolean(); @@ -61,9 +62,10 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster * * @param configuration for the Shoutcast stream */ - public ShoutcastV1AudioBroadcaster(ShoutcastV1Configuration configuration) + public ShoutcastV1AudioBroadcaster(ShoutcastV1Configuration configuration, AliasModel aliasModel) { super(configuration); + mAliasModel = aliasModel; } /** @@ -79,7 +81,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster { if(mMetadataUpdater == null) { - mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getConfiguration()); + mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getConfiguration(), mAliasModel); } return mMetadataUpdater; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1BroadcastMetadataUpdater.java b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1BroadcastMetadataUpdater.java index faf672d1..f1865ba4 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1BroadcastMetadataUpdater.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v1/ShoutcastV1BroadcastMetadataUpdater.java @@ -18,8 +18,15 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.shoutcast.v1; +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; import io.github.dsheirer.util.ThreadPool; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; @@ -50,7 +57,7 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp private final static String UTF8 = "UTF-8"; private ShoutcastV1Configuration mShoutcastV1Configuration; - + private AliasModel mAliasModel; private NioSocketConnector mSocketConnector; private AtomicBoolean mUpdating = new AtomicBoolean(); private Queue mMetadataQueue = new LinkedTransferQueue<>(); @@ -62,9 +69,10 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp * broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update * sequence, those updates will be queued and processed in the order received. */ - public ShoutcastV1BroadcastMetadataUpdater(ShoutcastV1Configuration shoutcastV1Configuration) + public ShoutcastV1BroadcastMetadataUpdater(ShoutcastV1Configuration shoutcastV1Configuration, AliasModel aliasModel) { mShoutcastV1Configuration = shoutcastV1Configuration; + mAliasModel = aliasModel; } /** @@ -111,9 +119,9 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp * and will remain in the update queue until the next metadata update is requested. However, this is a design * trade-off to avoid having a scheduled runnable repeatedly processing the update queue. */ - public void update(Metadata metadata) + public void update(IdentifierCollection identifierCollection) { - mMetadataQueue.offer(getSong(metadata)); + mMetadataQueue.offer(getSong(identifierCollection)); if(mUpdating.compareAndSet(false, true)) { @@ -177,45 +185,52 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp /** * Creates the song information for a metadata update */ - private static String getSong(Metadata metadata) + private String getSong(IdentifierCollection identifierCollection) { StringBuilder sb = new StringBuilder(); - if(metadata != null) + if(identifierCollection != null) { - String to = metadata.getPrimaryAddressTo().getIdentifier(); + AliasList aliasList = mAliasModel.getAliasList(identifierCollection); - sb.append("TO:"); + Identifier to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.PATCH_GROUP, Role.TO); - if(metadata.getPrimaryAddressTo().hasAlias()) + if(to == null) { - sb.append(metadata.getPrimaryAddressTo().getAlias().getName()); + to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO); } - else if(metadata.getPrimaryAddressTo().hasIdentifier()) + + Alias toAlias = aliasList != null ? aliasList.getAlias(to) : null; + + if(toAlias != null) { - sb.append(metadata.getPrimaryAddressTo().getIdentifier()); + sb.append("TO:").append(toAlias.getName()); + } + else if(to != null) + { + sb.append("TO:").append(to.toString()); } else { - sb.append("UNKNOWN"); + sb.append("TO:UNKNOWN"); } + Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM); - sb.append(" FROM:"); + Alias fromAlias = aliasList != null ? aliasList.getAlias(from) : null; - if(metadata.getPrimaryAddressFrom().hasAlias()) + if(fromAlias != null) { - sb.append(metadata.getPrimaryAddressFrom().getAlias().getName()); + sb.append(" FROM:").append(fromAlias.getName()); } - else if(metadata.getPrimaryAddressFrom().hasIdentifier()) + else if(from != null) { - sb.append(metadata.getPrimaryAddressFrom().getIdentifier()); + sb.append(" FROM:").append(from.toString()); } else { - sb.append("UNKNOWN"); + sb.append(" FROM:UNKNOWN"); } - } else { diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java index b0d3c377..ea31260a 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java @@ -18,6 +18,9 @@ ******************************************************************************/ package io.github.dsheirer.audio.broadcast.shoutcast.v2; +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.broadcast.AudioBroadcaster; import io.github.dsheirer.audio.broadcast.BroadcastState; import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater; @@ -36,7 +39,11 @@ import io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageF import io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageType; import io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox.UltravoxMetadata; import io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox.UltravoxProtocolFactory; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.util.ThreadPool; import org.apache.mina.core.RuntimeIoException; @@ -66,7 +73,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr private NioSocketConnector mSocketConnector; private IoSession mStreamingSession = null; - + private AliasModel mAliasModel; private long mLastConnectionAttempt = 0; private AtomicBoolean mConnecting = new AtomicBoolean(); private LinkedTransferQueue mMetadataMessageQueue = new LinkedTransferQueue<>(); @@ -83,9 +90,10 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr * * @param configuration for the Shoutcast V2 stream */ - public ShoutcastV2AudioBroadcaster(ShoutcastV2Configuration configuration) + public ShoutcastV2AudioBroadcaster(ShoutcastV2Configuration configuration, AliasModel aliasModel) { super(configuration); + mAliasModel = aliasModel; } public ShoutcastV2Configuration getConfiguration() @@ -119,9 +127,9 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr } @Override - public void update(Metadata metadata) + public void update(IdentifierCollection identifierCollection) { - List metadataMessages = getMetadataMessages(metadata); + List metadataMessages = getMetadataMessages(identifierCollection); if(!metadataMessages.isEmpty()) { @@ -241,10 +249,10 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr * * See UltravoxMetadata.TAG.asXML(String value) * - * @param metadata containing audio metadata tags and attributes + * @param identifierCollection containing audio metadata tags and attributes * @return a sequence of CacheableXMLMetadata messages sufficient to carry the complete set of metadata */ - private List getMetadataMessages(Metadata metadata) + private List getMetadataMessages(IdentifierCollection identifierCollection) { StringBuilder sb = new StringBuilder(); @@ -266,24 +274,37 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr sb.append(UltravoxMetadata.URL.asXML(getConfiguration().getURL())); } - if(metadata != null) + if(identifierCollection != null) { - if(metadata.getPrimaryAddressTo().hasAlias()) + AliasList aliasList = mAliasModel.getAliasList(identifierCollection); + + Identifier to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.PATCH_GROUP, Role.TO); + if(to == null) { - sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getAlias().getName())); - } - else if(metadata.getPrimaryAddressTo().hasIdentifier()) - { - sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getIdentifier())); + to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO); } - if(metadata.getPrimaryAddressFrom().hasAlias()) + Alias toAlias = aliasList != null ? aliasList.getAlias(to) : null; + + if(toAlias != null) { - sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + metadata.getPrimaryAddressFrom().getAlias().getName())); + sb.append(UltravoxMetadata.TITLE_2.asXML(toAlias.getName())); } - else if(metadata.getPrimaryAddressFrom().hasIdentifier()) + else if(to != null) { - sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + metadata.getPrimaryAddressFrom().getIdentifier())); + sb.append(UltravoxMetadata.TITLE_2.asXML(to.toString())); + } + + Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM); + Alias fromAlias = aliasList != null ? aliasList.getAlias(from) : null; + + if(fromAlias != null) + { + sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + fromAlias.getName())); + } + else if(from != null) + { + sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + from.toString())); } } else diff --git a/src/main/java/io/github/dsheirer/audio/invert/AudioInverter.java b/src/main/java/io/github/dsheirer/audio/invert/AudioInverter.java index 63b6b6d9..f91ae5f7 100644 --- a/src/main/java/io/github/dsheirer/audio/invert/AudioInverter.java +++ b/src/main/java/io/github/dsheirer/audio/invert/AudioInverter.java @@ -17,7 +17,6 @@ ******************************************************************************/ package io.github.dsheirer.audio.invert; -import io.github.dsheirer.audio.InversionFrequency; import io.github.dsheirer.dsp.filter.Filters; import io.github.dsheirer.dsp.filter.FloatFIRFilter; import io.github.dsheirer.dsp.mixer.IOscillator; diff --git a/src/main/java/io/github/dsheirer/audio/invert/InversionFrequency.java b/src/main/java/io/github/dsheirer/audio/invert/InversionFrequency.java new file mode 100644 index 00000000..5c0f4957 --- /dev/null +++ b/src/main/java/io/github/dsheirer/audio/invert/InversionFrequency.java @@ -0,0 +1,61 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.audio.invert; + +/** + * Standard voice inversion foldover frequencies + * + * Reference: http://en.wikipedia.org/wiki/Voice_inversion + */ +public enum InversionFrequency +{ + HZ_2500( 2500 ), + HZ_2550( 2550 ), + HZ_2600( 2600 ), + HZ_2632( 2632 ), + HZ_2675( 2675 ), + HZ_2718( 2718 ), + HZ_2800( 2800 ), + HZ_2868( 2868 ), + HZ_2950( 2950 ), + HZ_3023( 3023 ), + HZ_3075( 3075 ), + HZ_3107( 3107 ), + HZ_3150( 3150 ), + HZ_3196( 3196 ), + HZ_3333( 3333 ), + HZ_3339( 3339 ), + HZ_3400( 3400 ), + HZ_3496( 3496 ), + HZ_3729( 3729 ), + HZ_4096( 4096 ); + + private int mFrequency; + + private InversionFrequency( int frequency ) + { + mFrequency = frequency; + } + + public int getFrequency() + { + return mFrequency; + } +} diff --git a/src/main/java/io/github/dsheirer/audio/AudioChannelPanel.java b/src/main/java/io/github/dsheirer/audio/playback/AudioChannelPanel.java similarity index 53% rename from src/main/java/io/github/dsheirer/audio/AudioChannelPanel.java rename to src/main/java/io/github/dsheirer/audio/playback/AudioChannelPanel.java index 7e6c1aa3..fbf05360 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioChannelPanel.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioChannelPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,14 +15,24 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ -package io.github.dsheirer.audio; + * ***************************************************************************** + */ +package io.github.dsheirer.audio.playback; +import com.google.common.eventbus.Subscribe; import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.audio.output.AudioOutput; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.audio.AudioEvent; +import io.github.dsheirer.eventbus.MyEventBus; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.settings.ColorSetting; @@ -32,11 +43,15 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; -import java.awt.*; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Font; +import java.util.List; -public class AudioChannelPanel extends JPanel - implements Listener, SettingChangeListener +public class AudioChannelPanel extends JPanel implements Listener, SettingChangeListener { private static final long serialVersionUID = 1L; private static final Logger mLog = LoggerFactory.getLogger(AudioChannelPanel.class); @@ -56,27 +71,35 @@ public class AudioChannelPanel extends JPanel private IconManager mIconManager; private SettingsManager mSettingsManager; + private UserPreferences mUserPreferences; + private TalkgroupFormatPreference mTalkgroupFormatPreference; private AudioOutput mAudioOutput; private JLabel mMutedLabel = new JLabel("M"); private JLabel mChannelName = new JLabel(" "); - private JLabel mToAlias = new JLabel(" "); - private JLabel mTo = new JLabel("-----"); + private JLabel mIconLabel = new JLabel(" "); + private JLabel mIdentifierLabel = new JLabel("-----"); + private Identifier mIdentifier; + private Alias mAlias; private boolean mConfigured = false; + private AliasModel mAliasModel; - public AudioChannelPanel(IconManager iconManager, SettingsManager settingsManager, AudioOutput audioOutput) + public AudioChannelPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager, + AudioOutput audioOutput, AliasModel aliasModel) { mIconManager = iconManager; mSettingsManager = settingsManager; mSettingsManager.addListener(this); - + mAliasModel = aliasModel; + mUserPreferences = userPreferences; + mTalkgroupFormatPreference = mUserPreferences.getTalkgroupFormatPreference(); mAudioOutput = audioOutput; if(mAudioOutput != null) { mAudioOutput.addAudioEventListener(this); - mAudioOutput.setMetadataListener(new AudioMetadataProcessor()); + mAudioOutput.setIdentifierCollectionListener(new AudioMetadataProcessor()); } mBackgroundColor = SystemProperties.getInstance().get(PROPERTY_COLOR_BACKGROUND, Color.BLACK); @@ -87,8 +110,30 @@ public class AudioChannelPanel extends JPanel init(); } + /** + * Receives preference update notifications via the event bus + * @param preferenceType that was updated + */ + @Subscribe + public void preferenceUpdated(PreferenceType preferenceType) + { + if(preferenceType == PreferenceType.IDENTIFIER) + { + updateLabels(); + } + } + + public void dispose() + { + //Deregister from receiving preference update notifications + MyEventBus.getEventBus().unregister(this); + } + private void init() { + //Register to receive preference updates + MyEventBus.getEventBus().register(this); + setLayout(new MigLayout("align center center, insets 0 0 0 0", "[][][align right]0[grow,fill]", "")); setBackground(mBackgroundColor); @@ -103,13 +148,13 @@ public class AudioChannelPanel extends JPanel mChannelName.setForeground(mLabelColor); add(mChannelName); - mToAlias.setFont(mFont); - mToAlias.setForeground(mValueColor); - add(mToAlias); + mIconLabel.setFont(mFont); + mIconLabel.setForeground(mValueColor); + add(mIconLabel); - mTo.setFont(mFont); - mTo.setForeground(mValueColor); - add(mTo,"wmin 10lp"); + mIdentifierLabel.setFont(mFont); + mIdentifierLabel.setForeground(mValueColor); + add(mIdentifierLabel, "wmin 10lp"); } @Override @@ -149,72 +194,67 @@ public class AudioChannelPanel extends JPanel */ private void resetLabels() { - updateLabel(mTo, null, mToAlias, null); - + mIdentifier = null; + mAlias = null; + updateLabels(); mConfigured = false; } + /** + * Truncates the string to the specified length using an ellipses at the end + * @param value to optionally truncate + * @param length of the final formatted string + * @return formatted string or null + */ + private static String truncate(String value, int length) + { + if(value != null && value.length() > length) + { + return value.substring(0, length - 3) + "..."; + } + + return value; + } + /** * Updates the alias label with text and icon from the alias. Note: this * does not occur on the Swing event thread -- wrap any calls to this * method with an event thread call. */ - private void updateLabel(JLabel textLabel, String value, JLabel iconLabel, Alias alias) + private void updateLabels() { - if(value != null) + String identifier = mAlias != null ? mAlias.getName() : mTalkgroupFormatPreference.format(mIdentifier); + final ImageIcon icon = mAlias != null ? mIconManager.getIcon(mAlias.getIconName(), 18) : null; + final String identifierText = (identifier != null ? truncate(identifier, 33) : "-----"); + + EventQueue.invokeLater(new Runnable() { - - if(alias != null) + @Override + public void run() { - iconLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18)); - - String aliasName = alias.getName(); - - //Truncate label if length exceeds 33 characters - hack! - if(aliasName.length() > 33) - { - textLabel.setText(aliasName.substring(0,30) + "..."); - } - else - { - textLabel.setText(aliasName); - } + mIdentifierLabel.setText(identifierText); + mIconLabel.setIcon(icon); + mConfigured = true; } - else - { - iconLabel.setIcon(null); - textLabel.setText(value); - } - } - else - { - iconLabel.setIcon(null); - textLabel.setText("-----"); - } + }); + } /** * Processes audio metadata to update this panel's display values */ - public class AudioMetadataProcessor implements Listener + public class AudioMetadataProcessor implements Listener { @Override - public void receive(final Metadata metadata) + public void receive(final IdentifierCollection identifierCollection) { - if(metadata.isUpdated() || !mConfigured) + if(identifierCollection != null && (identifierCollection.isUpdated() || !mConfigured)) { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - updateLabel(mTo, metadata.getPrimaryAddressTo().getIdentifier(), - mToAlias, metadata.getPrimaryAddressTo().getAlias()); - - mConfigured = true; - } - }); - + List toIds = identifierCollection.getIdentifiers(IdentifierClass.USER, Role.TO); + mIdentifier = !toIds.isEmpty() ? toIds.get(0) : null; + AliasList aliasList = mAliasModel.getAliasList(identifierCollection); + mAlias = (aliasList != null ? aliasList.getAlias(mIdentifier) : null); + updateLabels(); } } } @@ -225,18 +265,18 @@ public class AudioChannelPanel extends JPanel { if(setting instanceof ColorSetting) { - ColorSetting colorSetting = (ColorSetting) setting; + ColorSetting colorSetting = (ColorSetting)setting; switch(colorSetting.getColorSettingName()) { case CHANNEL_STATE_LABEL_DECODER: - if(mTo != null) + if(mIdentifierLabel != null) { - mTo.setForeground(mLabelColor); + mIdentifierLabel.setForeground(mLabelColor); } - if(mToAlias != null) + if(mIconLabel != null) { - mToAlias.setForeground(mLabelColor); + mIconLabel.setForeground(mLabelColor); } break; default: diff --git a/src/main/java/io/github/dsheirer/audio/AudioChannelsPanel.java b/src/main/java/io/github/dsheirer/audio/playback/AudioChannelsPanel.java similarity index 58% rename from src/main/java/io/github/dsheirer/audio/AudioChannelsPanel.java rename to src/main/java/io/github/dsheirer/audio/playback/AudioChannelsPanel.java index a3db071b..42e77155 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioChannelsPanel.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioChannelsPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,24 +15,29 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ -package io.github.dsheirer.audio; + * ***************************************************************************** + */ +package io.github.dsheirer.audio.playback; -import io.github.dsheirer.audio.output.AudioOutput; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.audio.IAudioController; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.settings.SettingsManager; import net.miginfocom.swing.MigLayout; -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import java.awt.Color; +import java.awt.Component; import java.util.List; public class AudioChannelsPanel extends JPanel { private static final long serialVersionUID = 1L; - public AudioChannelsPanel(IconManager iconManager, SettingsManager settingsManager, IAudioController controller) + public AudioChannelsPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager, + IAudioController controller, AliasModel aliasModel) { setLayout(new MigLayout("insets 0 0 0 0", "[][sg abc,grow,fill][][sg abc,grow,fill]", "[grow,fill]")); @@ -43,7 +49,7 @@ public class AudioChannelsPanel extends JPanel for(int x = 0; x < outputs.size(); x++) { - add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x))); + add(new AudioChannelPanel(iconManager, userPreferences, settingsManager, outputs.get(x), aliasModel)); if(x < outputs.size() - 1) { @@ -56,7 +62,21 @@ public class AudioChannelsPanel extends JPanel if(outputs.size() == 1) { addSeparator(); - add(new AudioChannelPanel(iconManager, settingsManager, null), "growx"); + add(new AudioChannelPanel(iconManager, userPreferences, settingsManager, null, aliasModel), "growx"); + } + } + + /** + * Prepares for dispose to allow deregistering from services + */ + public void dispose() + { + for(Component component: getComponents()) + { + if(component instanceof AudioChannelPanel) + { + ((AudioChannelPanel)component).dispose(); + } } } diff --git a/src/main/java/io/github/dsheirer/audio/output/AudioOutput.java b/src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java similarity index 92% rename from src/main/java/io/github/dsheirer/audio/output/AudioOutput.java rename to src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java index fa0af9e6..45504cfd 100644 --- a/src/main/java/io/github/dsheirer/audio/output/AudioOutput.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,12 +15,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ -package io.github.dsheirer.audio.output; + * ***************************************************************************** + */ +package io.github.dsheirer.audio.playback; import io.github.dsheirer.audio.AudioEvent; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; @@ -54,7 +55,7 @@ public abstract class AudioOutput implements Listener, Line private int mBufferStartThreshold; private int mBufferStopThreshold; - private Listener mMetadataListener; + private Listener mIdentifierCollectionListener; private Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); private ScheduledFuture mProcessorTask; @@ -85,10 +86,7 @@ public abstract class AudioOutput implements Listener, Line * @param lineInfo to use when obtaining the source data line * @param requestedBufferSize of approximately 1 second of audio */ - public AudioOutput(Mixer mixer, - MixerChannel mixerChannel, - AudioFormat audioFormat, - Line.Info lineInfo, + public AudioOutput(Mixer mixer, MixerChannel mixerChannel, AudioFormat audioFormat, Line.Info lineInfo, int requestedBufferSize) { mMixer = mixer; @@ -174,7 +172,7 @@ public abstract class AudioOutput implements Listener, Line mAudioEventBroadcaster.dispose(); mAudioEventBroadcaster = null; - mMetadataListener = null; + mIdentifierCollectionListener = null; if(mOutput != null) { @@ -233,24 +231,24 @@ public abstract class AudioOutput implements Listener, Line * Registers a single listener to receive the audio metadata from each * audio packet */ - public void setMetadataListener(Listener listener) + public void setIdentifierCollectionListener(Listener listener) { - mMetadataListener = listener; + mIdentifierCollectionListener = listener; } public void removeAudioMetadataListener() { - mMetadataListener = null; + mIdentifierCollectionListener = null; } /** - * Broadcasts audio metadata to the registered listener + * Broadcasts audio identifier collection metadata to the registered listener */ - private void broadcast(Metadata metadata) + private void broadcast(IdentifierCollection identifierCollection) { - if(mMetadataListener != null) + if(mIdentifierCollectionListener != null) { - mMetadataListener.receive(metadata); + mIdentifierCollectionListener.receive(identifierCollection); } } @@ -312,7 +310,7 @@ public abstract class AudioOutput implements Listener, Line { if(packet.getType() == ReusableAudioPacket.Type.AUDIO) { - broadcast(packet.getMetadata()); + broadcast(packet.getIdentifierCollection()); ByteBuffer buffer = convert(packet); diff --git a/src/main/java/io/github/dsheirer/audio/AudioPanel.java b/src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java similarity index 92% rename from src/main/java/io/github/dsheirer/audio/AudioPanel.java rename to src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java index 66d80147..5b2333f4 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioPanel.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,12 +15,16 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ -package io.github.dsheirer.audio; + * ***************************************************************************** + */ +package io.github.dsheirer.audio.playback; -import io.github.dsheirer.audio.output.AudioOutput; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.audio.AudioEvent; +import io.github.dsheirer.audio.AudioException; +import io.github.dsheirer.audio.IAudioController; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.SourceManager; @@ -60,17 +65,21 @@ public class AudioPanel extends JPanel implements Listener private SettingsManager mSettingsManager; private SourceManager mSourceManager; private IAudioController mController; + private AliasModel mAliasModel; + private UserPreferences mUserPreferences; private JButton mMuteButton; private AudioChannelsPanel mAudioChannelsPanel; - public AudioPanel(IconManager iconManager, SettingsManager settingsManager, SourceManager sourceManager, - IAudioController controller) + public AudioPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager, + SourceManager sourceManager, IAudioController controller, AliasModel aliasModel) { mIconManager = iconManager; mSettingsManager = settingsManager; mSourceManager = sourceManager; mController = controller; + mAliasModel = aliasModel; + mUserPreferences = userPreferences; mController.addControllerListener(this); @@ -86,7 +95,7 @@ public class AudioPanel extends JPanel implements Listener mMuteButton.setBackground(getBackground()); add(mMuteButton); - mAudioChannelsPanel = new AudioChannelsPanel(mIconManager, mSettingsManager, mController); + mAudioChannelsPanel = new AudioChannelsPanel(mIconManager, mUserPreferences, mSettingsManager, mController, mAliasModel); add(mAudioChannelsPanel); @@ -107,13 +116,10 @@ public class AudioPanel extends JPanel implements Listener public void run() { remove(mAudioChannelsPanel); - - mAudioChannelsPanel = new AudioChannelsPanel(mIconManager, mSettingsManager, mController); - + mAudioChannelsPanel.dispose(); + mAudioChannelsPanel = new AudioChannelsPanel(mIconManager, mUserPreferences, mSettingsManager, mController, mAliasModel); add(mAudioChannelsPanel); - mAudioChannelsPanel.repaint(); - revalidate(); repaint(); } diff --git a/src/main/java/io/github/dsheirer/audio/AudioManager.java b/src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java similarity index 89% rename from src/main/java/io/github/dsheirer/audio/AudioManager.java rename to src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java index e48cf36a..449354d6 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioManager.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,13 +15,13 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ -package io.github.dsheirer.audio; + * ***************************************************************************** + */ +package io.github.dsheirer.audio.playback; -import io.github.dsheirer.audio.output.AudioOutput; -import io.github.dsheirer.audio.output.MonoAudioOutput; -import io.github.dsheirer.audio.output.StereoAudioOutput; +import io.github.dsheirer.audio.AudioEvent; +import io.github.dsheirer.audio.AudioException; +import io.github.dsheirer.audio.IAudioController; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; @@ -44,9 +45,9 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -public class AudioManager implements Listener, IAudioController +public class AudioPlaybackManager implements Listener, IAudioController { - private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class); + private static final Logger mLog = LoggerFactory.getLogger(AudioPlaybackManager.class); public static final int AUDIO_TIMEOUT = 1000; //1 second @@ -79,7 +80,7 @@ public class AudioManager implements Listener, IAudioContro * packets to any combination of outputs based on any alias audio routing * options specified by the user. */ - public AudioManager(MixerManager mixerManager) + public AudioPlaybackManager(MixerManager mixerManager) { mMixerManager = mixerManager; @@ -185,9 +186,9 @@ public class AudioManager implements Listener, IAudioContro for(AudioOutputConnection connection : mAudioOutputConnections) { - if(connection.isInactive() && mChannelConnectionMap.containsKey(connection.getChannelMetadataID())) + if(connection.isInactive() && mChannelConnectionMap.containsKey(connection.getAudioChannelId())) { - mChannelConnectionMap.remove(connection.getChannelMetadataID()); + mChannelConnectionMap.remove(connection.getAudioChannelId()); connection.disconnect(); mAvailableConnectionCount++; changed = true; @@ -365,12 +366,12 @@ public class AudioManager implements Listener, IAudioContro */ private AudioOutputConnection getConnection(ReusableAudioPacket audioPacket) { - int channelMetadataID = audioPacket.getMetadata().getMetadataID(); + int audioChannelId = audioPacket.getAudioChannelId(); //Use an existing connection - if(mChannelConnectionMap.containsKey(channelMetadataID)) + if(mChannelConnectionMap.containsKey(audioChannelId)) { - return mChannelConnectionMap.get(channelMetadataID); + return mChannelConnectionMap.get(audioChannelId); } //Connect to an unused, available connection @@ -380,8 +381,8 @@ public class AudioManager implements Listener, IAudioContro { if(connection.isDisconnected()) { - connection.connect(channelMetadataID, audioPacket.getMetadata().getAudioPriority()); - mChannelConnectionMap.put(channelMetadataID, connection); + connection.connect(audioChannelId, audioPacket.getMonitoringPriority()); + mChannelConnectionMap.put(audioChannelId, connection); mAvailableConnectionCount--; return connection; } @@ -390,17 +391,17 @@ public class AudioManager implements Listener, IAudioContro //Preempt an existing lower priority connection and connect when this is a higher priority packet else { - int priority = audioPacket.getMetadata().getAudioPriority(); + int priority = audioPacket.getMonitoringPriority(); AudioOutputConnection connection = mLowestPriorityConnection; if(connection != null && priority < connection.getPriority()) { - mChannelConnectionMap.remove(connection.getChannelMetadataID()); + mChannelConnectionMap.remove(connection.getAudioChannelId()); - connection.connect(channelMetadataID, priority); + connection.connect(audioChannelId, priority); - mChannelConnectionMap.put(channelMetadataID, connection); + mChannelConnectionMap.put(audioChannelId, connection); return connection; } @@ -427,7 +428,7 @@ public class AudioManager implements Listener, IAudioContro for(ReusableAudioPacket packet : mPackets) { /* Don't process any packet's marked as do not monitor */ - if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == ReusableAudioPacket.Type.AUDIO) + if(!packet.isDoNotMonitor() && packet.getType() == ReusableAudioPacket.Type.AUDIO) { AudioOutputConnection connection = getConnection(packet); @@ -469,11 +470,11 @@ public class AudioManager implements Listener, IAudioContro */ public class AudioOutputConnection { - private static final int DISCONNECTED = -1; + private static final int DISCONNECTED = Integer.MIN_VALUE; private AudioOutput mAudioOutput; private int mPriority = 0; - private int mChannelMetadataID = DISCONNECTED; + private int mAudioChannelId = DISCONNECTED; public AudioOutputConnection(AudioOutput audioOutput) { @@ -482,9 +483,9 @@ public class AudioManager implements Listener, IAudioContro public void receive(ReusableAudioPacket packet) { - if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID) + if(packet.hasIdentifierCollection() && packet.getAudioChannelId() == mAudioChannelId) { - int priority = packet.getMetadata().getAudioPriority(); + int priority = packet.getMonitoringPriority(); if(mPriority != priority) { @@ -499,10 +500,10 @@ public class AudioManager implements Listener, IAudioContro } else { - if(packet.hasMetadata()) + if(packet.hasIdentifierCollection()) { - mLog.error("Received audio packet from channel metadata [" + packet.getMetadata().getMetadataID() + - "] however this assignment is currently connected to metadata [" + mChannelMetadataID + "]"); + mLog.error("Received audio packet from channel metadata [" + packet.getAudioChannelId() + + "] however this assignment is currently connected to metadata [" + mAudioChannelId + "]"); } else { @@ -525,7 +526,7 @@ public class AudioManager implements Listener, IAudioContro */ public boolean isDisconnected() { - return mChannelMetadataID == DISCONNECTED; + return mAudioChannelId == DISCONNECTED; } /** @@ -542,7 +543,7 @@ public class AudioManager implements Listener, IAudioContro */ public void connect(int source, int priority) { - mChannelMetadataID = source; + mAudioChannelId = source; mPriority = priority; updateLowestPriorityAssignment(); @@ -552,9 +553,9 @@ public class AudioManager implements Listener, IAudioContro /** * Currently connected source or -1 if disconnected */ - public int getChannelMetadataID() + public int getAudioChannelId() { - return mChannelMetadataID; + return mAudioChannelId; } /** @@ -563,7 +564,7 @@ public class AudioManager implements Listener, IAudioContro */ public void disconnect() { - mChannelMetadataID = DISCONNECTED; + mAudioChannelId = DISCONNECTED; mPriority = 0; } diff --git a/src/main/java/io/github/dsheirer/audio/output/MonoAudioOutput.java b/src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java similarity index 98% rename from src/main/java/io/github/dsheirer/audio/output/MonoAudioOutput.java rename to src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java index b1722b5f..2fad8bfc 100644 --- a/src/main/java/io/github/dsheirer/audio/output/MonoAudioOutput.java +++ b/src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package io.github.dsheirer.audio.output; +package io.github.dsheirer.audio.playback; import io.github.dsheirer.audio.AudioFormats; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; diff --git a/src/main/java/io/github/dsheirer/audio/output/StereoAudioOutput.java b/src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java similarity index 98% rename from src/main/java/io/github/dsheirer/audio/output/StereoAudioOutput.java rename to src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java index 7a7cc828..c8538c7f 100644 --- a/src/main/java/io/github/dsheirer/audio/output/StereoAudioOutput.java +++ b/src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package io.github.dsheirer.audio.output; +package io.github.dsheirer.audio.playback; import io.github.dsheirer.audio.AudioFormats; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; diff --git a/src/main/java/io/github/dsheirer/bits/BinaryMessage.java b/src/main/java/io/github/dsheirer/bits/BinaryMessage.java index 8ce03554..3a3e2cc6 100644 --- a/src/main/java/io/github/dsheirer/bits/BinaryMessage.java +++ b/src/main/java/io/github/dsheirer/bits/BinaryMessage.java @@ -1,17 +1,17 @@ /******************************************************************************* * 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 ******************************************************************************/ @@ -24,14 +24,14 @@ import java.util.BitSet; public class BinaryMessage extends BitSet { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; /** * Logical (ie constructed) size of this bitset, despite the actual size of * the super bitset that this class is based on */ private int mSize = 0; - + /** * Pointer to the next fill index location, when adding bits to this bitset * one at a time. @@ -41,49 +41,49 @@ public class BinaryMessage extends BitSet /** * Bitset that buffers bits added one at a time, up to the size of the this * bitset. - * + * * Note: the super class bitset behind this class may have a size larger * that the size parameter specified. * @param size */ - + /** * Used for temporary storage of CRC check results when we're passing this * message to an EDAC function. */ private CRC mCRC; - - public BinaryMessage( int size ) + + public BinaryMessage(int size) { - super( size ); + super(size); mSize = size; } - + /** * Constructs a bitset buffer and preloads it with the bits contained in * the bitsToPreload parameter. If the bitsToPreload are longer than the * size of the bitset, only those bits that fit will be preloaded - * + * * @param size * @param bitsToPreload */ - public BinaryMessage( int size, boolean[] bitsToPreload ) + public BinaryMessage(int size, boolean[] bitsToPreload) { - this( size ); - + this(size); + int pointer = 0; - - while( !this.isFull() && pointer < bitsToPreload.length ) + + while(!this.isFull() && pointer < bitsToPreload.length) { try { - this.add( bitsToPreload[ pointer ] ); + this.add(bitsToPreload[pointer]); } - catch( BitSetFullException e ) + catch(BitSetFullException e) { e.printStackTrace(); } - + pointer++; } } @@ -91,17 +91,17 @@ public class BinaryMessage extends BitSet /** * Constructs a new BitSetBuffer from an existing one */ - private BinaryMessage( BinaryMessage toCopyFrom ) + private BinaryMessage(BinaryMessage toCopyFrom) { - this( toCopyFrom.size() ); - this.or( toCopyFrom ); + this(toCopyFrom.size()); + this.or(toCopyFrom); this.mPointer = toCopyFrom.pointer(); } - public BinaryMessage( BitSet bitset, int size ) + public BinaryMessage(BitSet bitset, int size) { - this( size ); - this.or( bitset ); + this(size); + this.or(bitset); this.mPointer = size - 1; } @@ -109,43 +109,43 @@ public class BinaryMessage extends BitSet { this(BitSet.valueOf(data), data.length * 8); } - + /** * Returns a mew binary message containing the bits from (inclusive) to * end (exclusive). - * + * * @param start * @param end * @return */ - public BinaryMessage getSubMessage( int start, int end ) + public BinaryMessage getSubMessage(int start, int end) { - BitSet subset = this.get( start, end ); - - return new BinaryMessage( subset, end - start ); + BitSet subset = this.get(start, end); + + return new BinaryMessage(subset, end - start); } - + public CRC getCRC() { - return mCRC; + return mCRC; } - - public void setCRC( CRC crc ) + + public void setCRC(CRC crc) { - mCRC = crc; + mCRC = crc; } /** * Indicates the number of bit errors that were corrected in this message. * - * @see CorrectedBinaryMessage subclass that supports setting this value * @return number of corrected bits in this message + * @see CorrectedBinaryMessage subclass that supports setting this value */ public int getCorrectedBitCount() { return 0; } - + /** * Current pointer index */ @@ -156,64 +156,66 @@ public class BinaryMessage extends BitSet /** * Sets the pointer to a specific value + * * @param index */ - public void setPointer( int index ) + public void setPointer(int index) { - mPointer = index; + mPointer = index; } /** - * Moves the current pointer position left (negative adjustment) or + * Moves the current pointer position left (negative adjustment) or * right (positive adjustment) */ - public void adjustPointer( int adjustment ) + public void adjustPointer(int adjustment) { - mPointer += adjustment; + mPointer += adjustment; } /** * Static method to construct a new BitSetBuffer, preloaded with the bits * from the preload parameter, and then filled with the bits from the * second bitsetbuffer parameter. - * + * * @param preloadBits - boolean array of bits to be prepended to the new - * bitset + * bitset * @param bitsetToAppend - full bitset to be appended to the residual bits array * @return - new Bitset preloaded with residual bits and new bitset */ - public static BinaryMessage merge( boolean[] preloadBits, BinaryMessage bitsetToAppend ) + public static BinaryMessage merge(boolean[] preloadBits, BinaryMessage bitsetToAppend) { - BinaryMessage returnValue = new BinaryMessage( preloadBits.length + bitsetToAppend.size(), preloadBits ); + BinaryMessage returnValue = new BinaryMessage(preloadBits.length + bitsetToAppend.size(), preloadBits); int pointer = 0; - - while( pointer < bitsetToAppend.size() && !returnValue.isFull() ) + + while(pointer < bitsetToAppend.size() && !returnValue.isFull()) { try { - returnValue.add( bitsetToAppend.get( pointer ) ); + returnValue.add(bitsetToAppend.get(pointer)); } - catch( BitSetFullException e ) + catch(BitSetFullException e) { e.printStackTrace(); } - + pointer++; } - + return returnValue; } - + /** * Returns a (new) copy of this bitsetbuffer + * * @return */ public BinaryMessage copy() { - return new BinaryMessage( this ); + return new BinaryMessage(this); } - + public boolean isFull() { return mPointer >= mSize; @@ -229,10 +231,10 @@ public class BinaryMessage extends BitSet { return mSize; } - - public void setSize( int size ) + + public void setSize(int size) { - mSize = size; + mSize = size; } /** @@ -242,41 +244,57 @@ public class BinaryMessage extends BitSet @Override public void clear() { - this.clear( 0, mSize ); + this.clear(0, mSize); mPointer = 0; } /** - * Adds a the bit parameters to this bitset, placing it in the index + * Adds a the bit parameters to this bitset, placing it in the index * specified by mPointer, and incrementing mPointer to prepare for the next * call to this method + * * @param value * @throws BitSetFullException - if the size specified at construction is - * exceeded. Invoke full() to determine if the bitset is full either before - * adding a new bit, or after adding a bit. + * exceeded. Invoke full() to determine if the bitset is full either before + * adding a new bit, or after adding a bit. */ - public void add( boolean value ) throws BitSetFullException + public void add(boolean value) throws BitSetFullException { - if( !isFull() ) + if(!isFull()) { - this.set( mPointer++, value ); + this.set(mPointer++, value); } else { - throw new BitSetFullException( "bitset is full -- contains " + - ( mPointer + 1 ) + "bits"); + throw new BitSetFullException("bitset is full -- contains " + + (mPointer + 1) + "bits"); } } - + public String toString() { StringBuilder sb = new StringBuilder(); - - for( int x = 0; x < mSize; x++ ) + + for(int x = 0; x < mSize; x++) { - sb.append( ( this.get( x ) ? "1" : "0" ) ); + sb.append((this.get(x) ? "1" : "0")); } - + + return sb.toString(); + } + + public String toHexString() + { + int pointer = 0; + + StringBuilder sb = new StringBuilder(); + + while(pointer < size()) + { + sb.append(getHex(pointer, pointer + 3, 1)); + pointer += 4; + } + return sb.toString(); } @@ -285,19 +303,20 @@ public class BinaryMessage extends BitSet */ public int[] toIntegerArray() { - int[] values = new int[ mSize ]; - - for (int i = nextSetBit( 0 ); i >= 0 && i < mSize; i = nextSetBit( i+1 ) ) - { - values[ i ] = 1; - } - - return values; + int[] values = new int[mSize]; + + for(int i = nextSetBit(0); i >= 0 && i < mSize; i = nextSetBit(i + 1)) + { + values[i] = 1; + } + + return values; } /** * Returns this message as a little endian byte array. Extra 0 bits will be padded to the end to make the overall * length a multiple of 8. + * * @return little endian message byte array */ public byte[] toByteArray() @@ -306,7 +325,7 @@ public class BinaryMessage extends BitSet if(length * 8 < size()) { - length ++; + length++; } byte[] bytes = new byte[length]; @@ -321,59 +340,59 @@ public class BinaryMessage extends BitSet return bytes; } - + /** * Returns this bitset as a reversed bit order array of integer ones and zeros * from the specified index range */ - public int[] toReverseIntegerArray( int start, int end ) + public int[] toReverseIntegerArray(int start, int end) { - int[] values = new int[ end - start + 1 ]; - - for (int i = nextSetBit( start ); i >= start && i <= end; i = nextSetBit( i+1 ) ) - { - values[ end - i ] = 1; - } - - return values; + int[] values = new int[end - start + 1]; + + for(int i = nextSetBit(start); i >= start && i <= end; i = nextSetBit(i + 1)) + { + values[end - i] = 1; + } + + return values; } - + /** * Returns a boolean array from startIndex to end of the bitset */ - public boolean[] getBits( int startIndex ) + public boolean[] getBits(int startIndex) { - return getBits( startIndex, mSize - 1 ); + return getBits(startIndex, mSize - 1); } - + /** * Returns a boolean array of the right-most bitCount number of bits */ - public boolean[] right( int bitCount ) + public boolean[] right(int bitCount) { - return getBits( mSize - bitCount - 1 ); + return getBits(mSize - bitCount - 1); } - + /** * Returns a boolean array representing the bits located from startIndex * through endIndex */ - public boolean[] getBits( int startIndex, int endIndex ) + public boolean[] getBits(int startIndex, int endIndex) { boolean[] returnValue = null; - - if( startIndex >= 0 && - startIndex < endIndex && - endIndex < mSize ) + + if(startIndex >= 0 && + startIndex < endIndex && + endIndex < mSize) { - returnValue = new boolean[ endIndex - startIndex + 1 ]; + returnValue = new boolean[endIndex - startIndex + 1]; int bitsetPointer = startIndex; int returnPointer = 0; - - while( bitsetPointer <= endIndex ) + + while(bitsetPointer <= endIndex) { - returnValue[ returnPointer ] = this.get( bitsetPointer ); + returnValue[returnPointer] = this.get(bitsetPointer); bitsetPointer++; returnPointer++; } @@ -381,42 +400,75 @@ public class BinaryMessage extends BitSet return returnValue; } - + /** * Returns the integer value represented by the bit array + * * @param bits - an array of bit positions that will be treated as if they - * were contiguous bits, with index 0 being the MSB and index - * length - 1 being the LSB + * were contiguous bits, with index 0 being the MSB and index + * length - 1 being the LSB * @return - integer value of the bit array */ - public int getInt( int[] bits ) + public int getInt(int[] bits) { - if( bits.length > 32 ) - { - throw new IllegalArgumentException( "Overflow - must be 32 bits " - + "or less to fit into a primitive integer value" ); - } + if(bits.length > 32) + { + throw new IllegalArgumentException("Overflow - must be 32 bits " + + "or less to fit into a primitive integer value"); + } - int value = 0; + int value = 0; - for( int index: bits ) - { - value = Integer.rotateLeft( value, 1 ); - - if( get( index ) ) - { - value++; - } - } - - return value; + for(int index : bits) + { + value = Integer.rotateLeft(value, 1); + + if(get(index)) + { + value++; + } + } + + return value; + } + + /** + * Returns the integer value represented by the bit array + * + * @param bits - an array of bit positions that will be treated as if they + * were contiguous bits, with index 0 being the MSB and index + * length - 1 being the LSB + * @param offset to apply to each of the bit position in the bits array. + * @return - integer value of the bit array + */ + public int getInt(int[] bits, int offset) + { + if(bits.length > 32) + { + throw new IllegalArgumentException("Overflow - must be 32 bits " + + "or less to fit into a primitive integer value"); + } + + int value = 0; + + for(int index : bits) + { + value = Integer.rotateLeft(value, 1); + + if(get(index + offset)) + { + value++; + } + } + + return value; } public void setInt(int value, int[] indices) { for(int x = 0; x < indices.length; x++) { - int mask = 1<<(indices.length - x - 1); + int mask = 1 << (indices.length - x - 1); if((value & mask) == mask) { @@ -431,36 +483,70 @@ public class BinaryMessage extends BitSet /** * Returns the byte value represented by the bit array + * * @param bits - an array of bit positions that will be treated as if they - * were contiguous bits, with index 0 being the MSB and index - * length - 1 being the LSB + * were contiguous bits, with index 0 being the MSB and index + * length - 1 being the LSB * @return - byte value of the bit array */ - public byte getByte( int[] bits ) + public byte getByte(int[] bits) { - if( bits.length != 8 ) - { - throw new IllegalArgumentException( "Invalid - there must be 8" - + "indexes to form a proper byte" ); - } + if(bits.length != 8) + { + throw new IllegalArgumentException("Invalid - there must be 8" + + "indexes to form a proper byte"); + } - int value = 0; + int value = 0; - for( int index: bits ) - { - value = Integer.rotateLeft( value, 1 ); - - if( get( index ) ) - { - value++; - } - } - - return (byte)( value & 0xFF ); + for(int index : bits) + { + value = Integer.rotateLeft(value, 1); + + if(get(index)) + { + value++; + } + } + + return (byte)(value & 0xFF); + } + + /** + * Returns the byte value represented by the bit array + * + * @param bits - an array of bit positions that will be treated as if they + * were contiguous bits, with index 0 being the MSB and index + * length - 1 being the LSB + * @param offset to apply to each of the bit positions + * @return - byte value of the bit array + */ + public byte getByte(int[] bits, int offset) + { + if(bits.length != 8) + { + throw new IllegalArgumentException("Invalid - there must be 8" + + "indexes to form a proper byte"); + } + + int value = 0; + + for(int index : bits) + { + value = Integer.rotateLeft(value, 1); + + if(get(index + offset)) + { + value++; + } + } + + return (byte)(value & 0xFF); } /** * Returns the byte value contained between index and index + 7 bit positions + * * @param index specifying the start of the byte value * @return byte value contained at index <> index + 7 bit positions */ @@ -512,390 +598,404 @@ public class BinaryMessage extends BitSet /** * Returns the long value represented by the bit array + * * @param bits - an array of bit positions that will be treated as if they - * were contiguous bits, with index 0 being the MSB and index - * length - 1 being the LSB + * were contiguous bits, with index 0 being the MSB and index + * length - 1 being the LSB * @return - integer value of the bit array */ - public long getLong( int[] bits ) + public long getLong(int[] bits) { - if( bits.length > 64 ) - { - throw new IllegalArgumentException( "Overflow - must be 64 bits " - + "or less to fit into a primitive long value" ); - } + if(bits.length > 64) + { + throw new IllegalArgumentException("Overflow - must be 64 bits " + + "or less to fit into a primitive long value"); + } - long value = 0; + long value = 0; - for( int index: bits ) - { - value = Long.rotateLeft( value, 1 ); - - if( get( index ) ) - { - value++; - } - } - - return value; + for(int index : bits) + { + value = Long.rotateLeft(value, 1); + + if(get(index)) + { + value++; + } + } + + return value; } - - + /** * Converts up to 63 bits from the bit array into an integer and then * formats the value into hexadecimal, prefixing the value with zeros to * provide a total length of digitDisplayCount; - * + * * @param bits * @param digitDisplayCount * @return */ - public String getHex( int[] bits, int digitDisplayCount ) + public String getHex(int[] bits, int digitDisplayCount) { - if( bits.length <= 32 ) - { - int value = getInt( bits ); - - return String.format( "%0" + digitDisplayCount + "X", value ); - } - else if( bits.length <= 64 ) - { - long value = getLong( bits ); - - return String.format( "%0" + digitDisplayCount + "X", value ); - } - else - { - throw new IllegalArgumentException( "BitSetBuffer.getHex() " - + "maximum array length is 63 bits" ); - } + if(bits.length <= 32) + { + int value = getInt(bits); + + return String.format("%0" + digitDisplayCount + "X", value); + } + else if(bits.length <= 64) + { + long value = getLong(bits); + + return String.format("%0" + digitDisplayCount + "X", value); + } + else + { + throw new IllegalArgumentException("BitSetBuffer.getHex() " + + "maximum array length is 63 bits"); + } } - public String getHex( int msb, int lsb, int digitDisplayCount ) + public String getHex(int msb, int lsb, int digitDisplayCount) { - int length = lsb - msb; - - if( length <= 32 ) - { - int value = getInt( msb, lsb ); - - return String.format( "%0" + digitDisplayCount + "X", value ); - } - else if( length <= 64 ) - { - long value = getLong( msb, lsb ); - - return String.format( "%0" + digitDisplayCount + "X", value ); - } - else - { - throw new IllegalArgumentException( "BitSetBuffer.getHex() " - + "maximum array length is 64 bits" ); - } + int length = lsb - msb; + + if(length <= 32) + { + int value = getInt(msb, lsb); + + return String.format("%0" + digitDisplayCount + "X", value); + } + else if(length <= 64) + { + long value = getLong(msb, lsb); + + return String.format("%0" + digitDisplayCount + "X", value); + } + else + { + throw new IllegalArgumentException("BitSetBuffer.getHex() " + + "maximum array length is 64 bits"); + } } /** * Returns the int value represented by the bit range. This method will * parse the bits in big endian or little endian format. The start value * represents the MSB and the end value represents the LSB of the value. - * + * * start < end: little endian interpretation * end < start: big endian interpretation - * + * * @param start - MSB of the value * @param end - LSB of the value - * * @return - int value of the bit range */ - public int getInt( int start, int end ) + public int getInt(int start, int end) { - if( Math.abs( end - start ) > 32 ) - { - throw new IllegalArgumentException( "Overflow - must be 32 bits " - + "or less to fit into a primitive integer value" ); - } - - int value = 0; + if(Math.abs(end - start) > 32) + { + throw new IllegalArgumentException("Overflow - must be 32 bits " + + "or less to fit into a primitive integer value"); + } - if( start < end ) - { - for( int x = start; x <= end; x++ ) - { - value = Integer.rotateLeft( value, 1 ); + int value = 0; - if( get( x ) ) - { - value++;; - } - } - } - else - { - for( int x = end; x >= start; x-- ) - { - value = Integer.rotateLeft( value, 1 ); + if(start < end) + { + for(int x = start; x <= end; x++) + { + value = Integer.rotateLeft(value, 1); - if( get( x ) ) - { - value++;; - } - } - } - - return value; + if(get(x)) + { + value++; + ; + } + } + } + else + { + for(int x = end; x >= start; x--) + { + value = Integer.rotateLeft(value, 1); + + if(get(x)) + { + value++; + ; + } + } + } + + return value; } - + /** * Returns the long value represented by the bit range. This method will * parse the bits in big endian or little endian format. The start value * represents the MSB and the end value represents the LSB of the value. - * + * * start < end: little endian interpretation * end < start: big endian interpretation - * + * * @param start - MSB of the value * @param end - LSB of the value - * * @return - long value of the bit range */ - public long getLong( int start, int end ) + public long getLong(int start, int end) { - if( Math.abs( end - start ) > 64 ) - { - throw new IllegalArgumentException( "Overflow - must be 64 bits " - + "or less to fit into a primitive long value" ); - } - - long value = 0; + if(Math.abs(end - start) > 64) + { + throw new IllegalArgumentException("Overflow - must be 64 bits " + + "or less to fit into a primitive long value"); + } - if( start < end ) - { - for( int x = start; x <= end; x++ ) - { - value = Long.rotateLeft( value, 1 ); - - if( get( x ) ) - { - value++; - } - } - } - else - { - for( int x = end; x >= start; x-- ) - { - value = Long.rotateLeft( value, 1 ); - - if( get( x ) ) - { - value++; - } - } - } - - return value; + long value = 0; + + if(start < end) + { + for(int x = start; x <= end; x++) + { + value = Long.rotateLeft(value, 1); + + if(get(x)) + { + value++; + } + } + } + else + { + for(int x = end; x >= start; x--) + { + value = Long.rotateLeft(value, 1); + + if(get(x)) + { + value++; + } + } + } + + return value; } /** * Creates a buffer of size=width and fills the buffer with the fill value + * * @param width - size of the buffer * @param fill - initial fill value * @return - filled buffer */ - public static BinaryMessage getBuffer( int width, long fill ) - { - BinaryMessage buffer = new BinaryMessage( width ); - - buffer.load( 0, width, fill ); - - return buffer; - } - - /** - * Loads the value into the buffer starting at the offset index, and - * assuming that the value represents (width) number of bits. The MSB of - * the value will be located at the offset and the LSB of the value will - * be located at ( offset + width ). - * - * @param offset - starting bit index for the MSB of the value - * @param width - representative bit width of the value - * @param value - value to be loaded into the buffer - */ - public void load( int offset, int width, long value ) - { - for( int x = 0; x < width; x++ ) - { - long mask = Long.rotateLeft( 1, width - x - 1 ); - - if( ( mask & value ) == mask ) - { - set( offset + x ); - } - else - { - clear( offset + x ); - } - } - } - - /** - * Generates an array of message bit position indexes to support accessing - * a contiguous field value - * - * @param start - starting bit position of the field - * @param length - field length - * @return - array of field indexes - */ - public static int[] getFieldIndexes( int start, int length, boolean bigEndian ) - { - int[] checksumIndexes = new int[ length ]; - - for( int x = 0; x < length; x++ ) - { - if( bigEndian ) - { - checksumIndexes[ length - x - 1 ] = start + x; - } - else - { - checksumIndexes[ x ] = start + x; - } - } - - return checksumIndexes; - } - - /** - * Creates a bitsetbuffer loaded from a string of zeros and ones - * - * @param message - string containing only zeros and ones - * @return - loaded buffer - */ - public static BinaryMessage load( String message ) - { - if( !message.matches( "[01]*" ) ) - { - throw new IllegalArgumentException( - "Message must contain only zeros and ones" ); - } - - BinaryMessage buffer = new BinaryMessage( message.length() ); - - for( int x = 0; x < message.length(); x++ ) - { - if( message.substring( x, x + 1 ).contentEquals( "1" ) ) - { - buffer.set( x ); - } - } - - return buffer; - } - - /** - * Left rotates the bits between start and end indices, number of places. - */ - public void rotateLeft( int places, int startIndex, int endIndex ) - { - for( int x = 0; x < places; x++ ) - { - rotateLeft( startIndex, endIndex ); - } - } - - /** - * Left rotates the bits between start and end and wraps the left-most - * bit around to the end. - */ - public void rotateLeft( int startIndex, int endIndex ) - { - boolean wrapBit = get( startIndex ); - - for( int x = startIndex; x < endIndex; x++ ) - { - if( get( x + 1 ) ) - { - set( x ); - } - else - { - clear( x ); - } - } - - if( wrapBit ) - { - set( endIndex ); - } - else - { - clear( endIndex ); - } - } - - /** - * Right rotates the bits between start and end indices, number of places. - */ - public void rotateRight( int places, int startIndex, int endIndex ) - { - for( int x = 0; x < places; x++ ) - { - rotateRight( startIndex, endIndex ); - } - } - - /** - * Right rotates the bits between start and end and wraps the right-most - * bit around to the start. - */ - public void rotateRight( int startIndex, int endIndex ) - { - boolean wrapBit = get( endIndex ); - - for( int x = endIndex - 1; x >= startIndex; x-- ) - { - if( get( x ) ) - { - set( x + 1 ); - } - else - { - clear( x + 1 ); - } - } - - if( wrapBit ) - { - set( startIndex ); - } - else - { - clear( startIndex ); - } - } - - /** - * Performs exclusive or of the value against this bitset starting at the - * offset position using width bits from the value. - */ - public void xor( int offset, int width, int value ) - { - BinaryMessage mask = new BinaryMessage( this.size() ); - - mask.load( offset, width, value ); - - this.xor( mask ); - } - - public static void main(String[] args) + public static BinaryMessage getBuffer(int width, long fill) { - BinaryMessage b = new BinaryMessage(32); + BinaryMessage buffer = new BinaryMessage(width); - int[] indices = {2,3,7,8}; + buffer.load(0, width, fill); - b.setInt(0xF, indices); + return buffer; + } - System.out.println(b.toString()); + /** + * Loads the value into the buffer starting at the offset index, and + * assuming that the value represents (width) number of bits. The MSB of + * the value will be located at the offset and the LSB of the value will + * be located at ( offset + width ). + * + * @param offset - starting bit index for the MSB of the value + * @param width - representative bit width of the value + * @param value - value to be loaded into the buffer + */ + public void load(int offset, int width, long value) + { + for(int x = 0; x < width; x++) + { + long mask = Long.rotateLeft(1, width - x - 1); + + if((mask & value) == mask) + { + set(offset + x); + } + else + { + clear(offset + x); + } + } + } + + /** + * Loads the binary message into this message starting at the offset. Note: this method + * does not do any length checking for the message being loaded and if that message is longer + * than this message, it would be loaded outside of the size (length) boundary of this message, but will not + * produce an error. + * + * @param offset into this message to load the binary message + * @param binaryMessage to load + */ + public void load(int offset, BinaryMessage binaryMessage) + { + for(int x = 0; x < binaryMessage.size(); x++) + { + if(binaryMessage.get(x)) + { + set(x + offset); + } + else + { + clear(x + offset); + } + } + } + + /** + * Generates an array of message bit position indexes to support accessing + * a contiguous field value + * + * @param start - starting bit position of the field + * @param length - field length + * @return - array of field indexes + */ + public static int[] getFieldIndexes(int start, int length, boolean bigEndian) + { + int[] checksumIndexes = new int[length]; + + for(int x = 0; x < length; x++) + { + if(bigEndian) + { + checksumIndexes[length - x - 1] = start + x; + } + else + { + checksumIndexes[x] = start + x; + } + } + + return checksumIndexes; + } + + /** + * Creates a bitsetbuffer loaded from a string of zeros and ones + * + * @param message - string containing only zeros and ones + * @return - loaded buffer + */ + public static BinaryMessage load(String message) + { + if(!message.matches("[01]*")) + { + throw new IllegalArgumentException( + "Message must contain only zeros and ones"); + } + + BinaryMessage buffer = new BinaryMessage(message.length()); + + for(int x = 0; x < message.length(); x++) + { + if(message.substring(x, x + 1).contentEquals("1")) + { + buffer.set(x); + } + } + + return buffer; + } + + /** + * Left rotates the bits between start and end indices, number of places. + */ + public void rotateLeft(int places, int startIndex, int endIndex) + { + for(int x = 0; x < places; x++) + { + rotateLeft(startIndex, endIndex); + } + } + + /** + * Left rotates the bits between start and end and wraps the left-most + * bit around to the end. + */ + public void rotateLeft(int startIndex, int endIndex) + { + boolean wrapBit = get(startIndex); + + for(int x = startIndex; x < endIndex; x++) + { + if(get(x + 1)) + { + set(x); + } + else + { + clear(x); + } + } + + if(wrapBit) + { + set(endIndex); + } + else + { + clear(endIndex); + } + } + + /** + * Right rotates the bits between start and end indices, number of places. + */ + public void rotateRight(int places, int startIndex, int endIndex) + { + for(int x = 0; x < places; x++) + { + rotateRight(startIndex, endIndex); + } + } + + /** + * Right rotates the bits between start and end and wraps the right-most + * bit around to the start. + */ + public void rotateRight(int startIndex, int endIndex) + { + boolean wrapBit = get(endIndex); + + for(int x = endIndex - 1; x >= startIndex; x--) + { + if(get(x)) + { + set(x + 1); + } + else + { + clear(x + 1); + } + } + + if(wrapBit) + { + set(startIndex); + } + else + { + clear(startIndex); + } + } + + /** + * Performs exclusive or of the value against this bitset starting at the + * offset position using width bits from the value. + */ + public void xor(int offset, int width, int value) + { + BinaryMessage mask = new BinaryMessage(this.size()); + + mask.load(offset, width, value); + + this.xor(mask); } } diff --git a/src/main/java/io/github/dsheirer/bits/CorrectedBinaryMessage.java b/src/main/java/io/github/dsheirer/bits/CorrectedBinaryMessage.java index a06f45af..a0465be1 100644 --- a/src/main/java/io/github/dsheirer/bits/CorrectedBinaryMessage.java +++ b/src/main/java/io/github/dsheirer/bits/CorrectedBinaryMessage.java @@ -62,4 +62,14 @@ public class CorrectedBinaryMessage extends BinaryMessage { mCorrectedBitCount = count; } + + /** + * Increases the current corrected bit count by the argument value. + * @param additionalCount to add to the current corrected bit count + */ + public void incrementCorrectedBitCount(int additionalCount) + { + mCorrectedBitCount += additionalCount; + } + } diff --git a/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java b/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java index a46185ad..83c71e75 100644 --- a/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java +++ b/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java @@ -1,21 +1,25 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.bits; public interface IBinarySymbolProcessor { - void receive(boolean symbol); + void process(boolean symbol); } diff --git a/src/main/java/io/github/dsheirer/bits/MessageFramer.java b/src/main/java/io/github/dsheirer/bits/MessageFramer.java index 27bdcabe..97fb43e4 100644 --- a/src/main/java/io/github/dsheirer/bits/MessageFramer.java +++ b/src/main/java/io/github/dsheirer/bits/MessageFramer.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.bits; import io.github.dsheirer.dsp.symbol.ISyncDetectListener; @@ -37,9 +41,9 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, private boolean[] mSyncPattern; private int mMessageLength; private ISyncDetectListener mSyncDetectListener; - private Broadcaster mBroadcaster = new Broadcaster(); - private List mMessageAssemblers = new ArrayList(); - private List mCompletedMessageAssemblers = new ArrayList(); + private Broadcaster mBroadcaster = new Broadcaster<>(); + private List mMessageAssemblers = new ArrayList<>(); + private List mCompletedMessageAssemblers = new ArrayList<>(); private SyncPatternMatcher mMatcher; public MessageFramer(boolean[] syncPattern, int messageLength) @@ -63,10 +67,9 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, { mBroadcaster.dispose(); mCompletedMessageAssemblers.clear(); - mMessageAssemblers.clear(); } - public void receive(boolean bit) + public void process(boolean bit) { mMatcher.receive(bit); @@ -99,7 +102,7 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, /* Notify any sync detect listener(s) */ if(mSyncDetectListener != null) { - mSyncDetectListener.syncDetected(); + mSyncDetectListener.syncDetected(0); } } } @@ -108,20 +111,7 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, @Override public void receive(Boolean bit) { - receive(bit); - } - - /** - * Causes all messages currently under assembly to be forcibly - * sent (ie flushed) to all registered message listeners, and - * subsequently, all assemblers to be deleted - */ - public void flush() - { - for(MessageAssembler assembler : mMessageAssemblers) - { - assembler.flush(); - } + process(bit); } @Override @@ -134,12 +124,12 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, * Allow a message listener to register with this framer to receive * all framed messages */ - public void addMessageListener(Listener listener) + public void addMessageListener(Listener listener) { mBroadcaster.addListener(listener); } - public void removeMessageListener(Listener listener) + public void removeMessageListener(Listener listener) { mBroadcaster.removeListener(listener); } @@ -152,7 +142,6 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, mMessageAssemblers.add(assembler); } - @SuppressWarnings("unused") private void removeMessageAssembler(MessageAssembler assembler) { mMessageAssemblers.remove(assembler); @@ -169,12 +158,12 @@ public class MessageFramer implements IBinarySymbolProcessor, Listener, */ private class MessageAssembler implements Listener { - BinaryMessage mMessage; + CorrectedBinaryMessage mMessage; boolean mComplete = false; MessageAssembler(int messageLength) { - mMessage = new BinaryMessage(messageLength); + mMessage = new CorrectedBinaryMessage(messageLength); } MessageAssembler(int messageLength, boolean[] initialFill) diff --git a/src/main/java/io/github/dsheirer/bits/MultiSyncPatternMatcher.java b/src/main/java/io/github/dsheirer/bits/MultiSyncPatternMatcher.java index 4b4e8621..89e939d3 100644 --- a/src/main/java/io/github/dsheirer/bits/MultiSyncPatternMatcher.java +++ b/src/main/java/io/github/dsheirer/bits/MultiSyncPatternMatcher.java @@ -88,7 +88,6 @@ public class MultiSyncPatternMatcher { if(processor.checkSync(mBits)) { - mSyncDetectListener.syncDetected(); mBitCount = 0; } } @@ -100,6 +99,15 @@ public class MultiSyncPatternMatcher } } + /** + * Returns the number of bit errors for the current value against the sync pattern. + * @return + */ + public long getCurrentValue() + { + return mBits; + } + /** * Processes one bit before checking sync processors for a match. */ diff --git a/src/main/java/io/github/dsheirer/bits/SoftSyncDetector.java b/src/main/java/io/github/dsheirer/bits/SoftSyncDetector.java index 6362e160..4322dbad 100644 --- a/src/main/java/io/github/dsheirer/bits/SoftSyncDetector.java +++ b/src/main/java/io/github/dsheirer/bits/SoftSyncDetector.java @@ -16,22 +16,19 @@ package io.github.dsheirer.bits; import io.github.dsheirer.dsp.symbol.ISyncDetectListener; +import org.apache.commons.lang3.Validate; public class SoftSyncDetector implements ISyncProcessor { private ISyncDetectListener mListener; private long mPattern; private int mThreshold; + private int mBitErrorCount; - public SoftSyncDetector(ISyncDetectListener listener, long pattern, int threshold) + public SoftSyncDetector(long pattern, int threshold, ISyncDetectListener listener) { - this(pattern, threshold); - + Validate.notNull(listener, "Sync detect listener cannot be null"); mListener = listener; - } - - public SoftSyncDetector(long pattern, int threshold) - { mPattern = pattern; mThreshold = threshold; } @@ -46,12 +43,21 @@ public class SoftSyncDetector implements ISyncProcessor { long difference = value ^ mPattern; - if((difference == 0 || Long.bitCount(difference) <= mThreshold) && - mListener != null) + if(difference == 0) { - mListener.syncDetected(); + mListener.syncDetected(0); return true; } + else + { + mBitErrorCount = Long.bitCount(difference); + + if(mBitErrorCount <= mThreshold) + { + mListener.syncDetected(mBitErrorCount); + return true; + } + } return false; } diff --git a/src/main/java/io/github/dsheirer/bits/SyncDetector.java b/src/main/java/io/github/dsheirer/bits/SyncDetector.java index aedaceec..25a8d30e 100644 --- a/src/main/java/io/github/dsheirer/bits/SyncDetector.java +++ b/src/main/java/io/github/dsheirer/bits/SyncDetector.java @@ -46,7 +46,7 @@ public class SyncDetector implements ISyncProcessor { if(mListener != null) { - mListener.syncDetected(); + mListener.syncDetected(0); } return true; diff --git a/src/main/java/io/github/dsheirer/channel/IChannelDescriptor.java b/src/main/java/io/github/dsheirer/channel/IChannelDescriptor.java new file mode 100644 index 00000000..18a35963 --- /dev/null +++ b/src/main/java/io/github/dsheirer/channel/IChannelDescriptor.java @@ -0,0 +1,67 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.channel; + +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.protocol.Protocol; + +/** + * Channel descriptor + */ +public interface IChannelDescriptor +{ + /** + * Downlink frequency for the channel + * @return frequency in hertz + */ + long getDownlinkFrequency(); + + /** + * Uplink frequency for the channel + * @return frequency in hertz + */ + long getUplinkFrequency(); + + /** + * Frequency band identifiers (0 - 15) + */ + int[] getFrequencyBandIdentifiers(); + + /** + * Assigns the frequency band message to the channel to use in calculating the up/downlink frequencies + * @param bandIdentifier + */ + void setFrequencyBand(IFrequencyBand bandIdentifier); + + /** + * Indicates if the timeslot count is greater than 1; + */ + boolean isTDMAChannel(); + + /** + * Number of timeslots. FDMA has 1 timeslot and TDMA has at least 2 timeslots + */ + int getTimeslotCount(); + + /** + * Protocol associated with the channel + */ + Protocol getProtocol(); +} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/AliasedIdentifier.java b/src/main/java/io/github/dsheirer/channel/metadata/AliasedIdentifier.java deleted file mode 100644 index a9bf91f2..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/AliasedIdentifier.java +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -import io.github.dsheirer.alias.Alias; - -public class AliasedIdentifier -{ - private String mIdentifier; - private Alias mAlias; - - /** - * Identifier and corresponding optional alias - */ - public AliasedIdentifier() - { - } - - /** - * Resets alias and value to null - * - * Package Private - should only be changed within MutableMetadata - */ - void reset() - { - mIdentifier = null; - mAlias = null; - } - - /** - * Identifier - */ - public String getIdentifier() - { - return mIdentifier; - } - - /** - * Sets the identifier to the argument value - * - * Package Private - should only be changed within MutableMetadata - */ - void setIdentifier(String identifier) - { - mIdentifier = identifier; - } - - /** - * Indicates if there is a non-null, non-empty identifier - */ - public boolean hasIdentifier() - { - return mIdentifier != null && !mIdentifier.isEmpty(); - } - - /** - * Alias for the identifier. - */ - public Alias getAlias() - { - return mAlias; - } - - /** - * Sets the alias to the argument - * - * Package Private - should only be changed within MutableMetadata - */ - void setAlias(Alias alias) - { - mAlias = alias; - } - - /** - * Indicates if there is an alias corresponding to the identifier - */ - public boolean hasAlias() - { - return mAlias != null; - } - - public AliasedIdentifier copyOf() - { - AliasedIdentifier copy = new AliasedIdentifier(); - - copy.setAlias(mAlias); - - if(mIdentifier != null) - { - copy.setIdentifier(new String(mIdentifier)); - } - - return copy; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/AliasedIntegerAttributeMonitor.java b/src/main/java/io/github/dsheirer/channel/metadata/AliasedIntegerAttributeMonitor.java deleted file mode 100644 index a79bfaf7..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/AliasedIntegerAttributeMonitor.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.sample.Listener; - -import static io.github.dsheirer.alias.id.AliasIDType.LTR_NET_UID; -import static io.github.dsheirer.alias.id.AliasIDType.STATUS; - - -public class AliasedIntegerAttributeMonitor extends AttributeMonitor -{ - private static final AliasIDType[] VALID_ID_TYPES = {STATUS, LTR_NET_UID}; - - private AliasList mAliasList; - private AliasIDType mAliasIDType; - - /** - * Provides monitoring of a String attribute's values with occurrence heuristics enabled and alias lookup with each - * change in the attribute's value. - * - * @param attribute to monitor - * @param listener to receive change requests - * @param aliasList to lookup alias from - * @param aliasIDType to determine correct alias lookup method in alias list - */ - public AliasedIntegerAttributeMonitor(Attribute attribute, Listener listener, AliasList aliasList, - AliasIDType aliasIDType) - { - super(attribute, listener); - - if(!isValidIDType(aliasIDType)) - { - throw new IllegalArgumentException("Alias ID Type [" + aliasIDType.name() + "] is not supported"); - } - mAliasList = aliasList; - mAliasIDType = aliasIDType; - } - - /** - * Indicates if the alias ID type is a supported/valid type for this monitor. - */ - public static boolean isValidIDType(AliasIDType type) - { - for(AliasIDType validType: VALID_ID_TYPES) - { - if(type == validType) - { - return true; - } - } - - return false; - } - - /** - * Lookup of the alias that corresponds to the value's alias ID type - */ - @Override - protected Alias getAlias() - { - Alias alias = null; - - if(mAliasList != null) - { - if(hasValue()) - { - switch(mAliasIDType) - { - case STATUS: - alias = mAliasList.getStatus(getValue()); - break; - case LTR_NET_UID: - alias = mAliasList.getUniqueID(getValue()); - break; - } - } - } - - return alias; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/AliasedStringAttributeMonitor.java b/src/main/java/io/github/dsheirer/channel/metadata/AliasedStringAttributeMonitor.java deleted file mode 100644 index 1f224478..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/AliasedStringAttributeMonitor.java +++ /dev/null @@ -1,121 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.sample.Listener; - -import static io.github.dsheirer.alias.id.AliasIDType.ESN; -import static io.github.dsheirer.alias.id.AliasIDType.FLEETSYNC; -import static io.github.dsheirer.alias.id.AliasIDType.MDC1200; -import static io.github.dsheirer.alias.id.AliasIDType.MIN; -import static io.github.dsheirer.alias.id.AliasIDType.MPT1327; -import static io.github.dsheirer.alias.id.AliasIDType.SITE; -import static io.github.dsheirer.alias.id.AliasIDType.TALKGROUP; - - -public class AliasedStringAttributeMonitor extends AttributeMonitor -{ - private static final AliasIDType[] VALID_ID_TYPES = {ESN, FLEETSYNC, MDC1200, MIN, MPT1327, SITE, TALKGROUP}; - - private AliasList mAliasList; - private AliasIDType mAliasIDType; - - /** - * Provides monitoring of a String attribute's values with occurrence heuristics enabled and alias lookup with each - * change in the attribute's value. - * - * @param attribute to monitor - * @param listener to receive change requests - * @param aliasList to lookup alias from - * @param aliasIDType to determine correct alias lookup method in alias list - */ - public AliasedStringAttributeMonitor(Attribute attribute, Listener listener, AliasList aliasList, - AliasIDType aliasIDType) - { - super(attribute, listener); - - if(!isValidIDType(aliasIDType)) - { - throw new IllegalArgumentException("Alias ID Type [" + aliasIDType.name() + "] is not supported"); - } - mAliasList = aliasList; - mAliasIDType = aliasIDType; - } - - /** - * Indicates if the alias ID type is a supported/valid type for this monitor. - */ - public static boolean isValidIDType(AliasIDType type) - { - for(AliasIDType validType: VALID_ID_TYPES) - { - if(type == validType) - { - return true; - } - } - - return false; - } - - /** - * Lookup of the alias that corresponds to the value's alias ID type - */ - @Override - protected Alias getAlias() - { - Alias alias = null; - - if(mAliasList != null) - { - if(hasValue()) - { - switch(mAliasIDType) - { - case ESN: - alias = mAliasList.getESNAlias(getValue()); - break; - case FLEETSYNC: - alias = mAliasList.getFleetsyncAlias(getValue()); - break; - case MDC1200: - alias = mAliasList.getMDC1200Alias(getValue()); - break; - case MIN: - alias = mAliasList.getMobileIDNumberAlias(getValue()); - break; - case MPT1327: - alias = mAliasList.getMPT1327Alias(getValue()); - break; - case SITE: - alias = mAliasList.getSiteID(getValue()); - break; - case TALKGROUP: - alias = mAliasList.getTalkgroupAlias(getValue()); - break; - } - } - } - - return alias; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/AttributeMonitor.java b/src/main/java/io/github/dsheirer/channel/metadata/AttributeMonitor.java deleted file mode 100644 index 6bc79da3..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/AttributeMonitor.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.sample.Listener; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class AttributeMonitor -{ - private Attribute mAttribute; - private Listener mListener; - private boolean mHeuristicsEnabled = true; - private Map mOccurrenceCounts = new HashMap<>(); - private T mCurrentValue; - private List mIllegalValues = new ArrayList<>(); - - public AttributeMonitor(Attribute attribute, Listener listener) - { - mAttribute = attribute; - mListener = listener; - } - - /** - * Processes the value and emits a change request if the value needs to be changed. If heuristics are enabled, - * an internal mapping of values to occurrence counts is updated and a change is emitted once a value that is - * different from the current value, has a higher occurrence count than the current value. If heuristics are - * disabled, then a change is emitted at each change in the value. - * - * @param t type - * @return true if the value was changed after processing - */ - public boolean process(T t) - { - boolean changed = false; - - if(t != null && !mIllegalValues.contains(t)) - { - if(mHeuristicsEnabled) - { - if(mOccurrenceCounts.containsKey(t)) - { - mOccurrenceCounts.put(t, mOccurrenceCounts.get(t) + 1); - } - else - { - mOccurrenceCounts.put(t, 1); - } - } - - if(mCurrentValue == null) - { - mCurrentValue = t; - changed = true; - } - else if(!mCurrentValue.equals(t)) - { - if(mHeuristicsEnabled) - { - int count = mOccurrenceCounts.get(mCurrentValue); - - for(Map.Entry entry : mOccurrenceCounts.entrySet()) - { - if(entry.getValue() > count) - { - count = entry.getValue(); - mCurrentValue = entry.getKey(); - changed = true; - } - } - } - else - { - mCurrentValue = t; - changed = true; - } - } - - if(changed && mListener != null) - { - mListener.receive(new AttributeChangeRequest(mAttribute, getValue(), getAlias())); - } - } - - return changed; - } - - /** - * Alias for the current value. This class does not return an alias, but subclasses can return an alias that - * is appropriate for the monitored attribute value. - * - * @return an optional alias for the current value - */ - protected Alias getAlias() - { - return null; - } - - /** - * Resets the attribute value to null and resets occurrence heuristics. - */ - public void reset() - { - mCurrentValue = null; - mOccurrenceCounts.clear(); - - if(mListener != null) - { - mListener.receive(new AttributeChangeRequest(mAttribute, null, null)); - } - } - - /** - * Adds a value to the internal list of illegal values. The monitor will not allow the attribute to be changed to - * any values contained in this list. - * - * @param illegalValue to prevent for the monitored alias - */ - public void addIllegalValue(T illegalValue) - { - mIllegalValues.add(illegalValue); - } - - /** - * Returns the current value or null if the monitor is newly started or reset. - */ - public T getValue() - { - return mCurrentValue; - } - - /** - * Indicates if the current value is non-null - */ - public boolean hasValue() - { - return mCurrentValue != null; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java new file mode 100644 index 00000000..591b1cbc --- /dev/null +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java @@ -0,0 +1,336 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.channel.metadata; + +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierUpdateListener; +import io.github.dsheirer.identifier.IdentifierUpdateNotification; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.ChannelNameConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.DecoderTypeConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier; +import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier; +import io.github.dsheirer.identifier.decoder.DecoderStateIdentifier; +import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel metadata containing details about the channel configuration, decoder state and current + * set of decoded user identifiers (ie TO and FROM). + */ +public class ChannelMetadata implements Listener, IdentifierUpdateListener +{ + private final static Logger mLog = LoggerFactory.getLogger(ChannelMetadata.class); + + private SystemConfigurationIdentifier mSystemConfigurationIdentifier; + private SiteConfigurationIdentifier mSiteConfigurationIdentifier; + private ChannelNameConfigurationIdentifier mChannelNameConfigurationIdentifier; + private FrequencyConfigurationIdentifier mFrequencyConfigurationIdentifier; + private DecoderStateIdentifier mDecoderStateIdentifier = DecoderStateIdentifier.IDLE; + private DecoderLogicalChannelNameIdentifier mDecoderLogicalChannelNameIdentifier; + private DecoderTypeConfigurationIdentifier mDecoderTypeConfigurationIdentifier; + private Identifier mFromIdentifier; + private Alias mFromIdentifierAlias; + private Identifier mToIdentifier; + private Alias mToIdentifierAlias; + + private IChannelMetadataUpdateListener mIChannelMetadataUpdateListener; + private AliasModel mAliasModel; + private AliasList mAliasList; + + public ChannelMetadata(AliasModel aliasModel) + { + mAliasModel = aliasModel; + } + + @Override + public Listener getIdentifierUpdateListener() + { + return this; + } + + /** + * System configuration identifier + */ + public SystemConfigurationIdentifier getSystemConfigurationIdentifier() + { + return mSystemConfigurationIdentifier; + } + + public boolean hasSystemConfigurationIdentifier() + { + return mSystemConfigurationIdentifier != null; + } + + /** + * Site configuration identifier + */ + public SiteConfigurationIdentifier getSiteConfigurationIdentifier() + { + return mSiteConfigurationIdentifier; + } + + public boolean hasSiteConfigurationIdentifier() + { + return mSiteConfigurationIdentifier != null; + } + + /** + * Channel configuration identifier + */ + public ChannelNameConfigurationIdentifier getChannelNameConfigurationIdentifier() + { + return mChannelNameConfigurationIdentifier; + } + + public boolean hasChannelConfigurationIdentifier() + { + return mChannelNameConfigurationIdentifier != null; + } + + /** + * Logical channel name/number expoded by the decoder + */ + public DecoderLogicalChannelNameIdentifier getDecoderLogicalChannelNameIdentifier() + { + return mDecoderLogicalChannelNameIdentifier; + } + + public boolean hasDecoderLogicalChannelNameIdentifier() + { + return mDecoderLogicalChannelNameIdentifier != null; + } + + /** + * Frequency configuration identifier + */ + public FrequencyConfigurationIdentifier getFrequencyConfigurationIdentifier() + { + return mFrequencyConfigurationIdentifier; + } + + public boolean hasFrequencyConfigurationIdentifier() + { + return mFrequencyConfigurationIdentifier != null; + } + + /** + * Decoder state identifier + */ + public DecoderStateIdentifier getDecoderStateIdentifier() + { + return mDecoderStateIdentifier; + } + + public boolean hasDecoderStateIdentifier() + { + return mDecoderStateIdentifier != null; + } + + /** + * Decoder type identifier + */ + public DecoderTypeConfigurationIdentifier getDecoderTypeConfigurationIdentifier() + { + return mDecoderTypeConfigurationIdentifier; + } + + public boolean hasDecoderTypeIdentifier() + { + return mDecoderTypeConfigurationIdentifier != null; + } + + /** + * Current call event FROM identifier + */ + public Identifier getFromIdentifier() + { + return mFromIdentifier; + } + + public boolean hasFromIdentifier() + { + return mFromIdentifier != null; + } + + /** + * Optional alias associated with the FROM identifier + */ + public Alias getFromIdentifierAlias() + { + return mFromIdentifierAlias; + } + + /** + * Current call event TO identifier + */ + public Identifier getToIdentifier() + { + return mToIdentifier; + } + + public boolean hasToIdentifier() + { + return mToIdentifier != null; + } + + /** + * Optional alias associated with the TO identifier + */ + public Alias getToIdentifierAlias() + { + return mToIdentifierAlias; + } + + /** + * Registers the listener for receiving field update events + */ + public void setUpdateEventListener(IChannelMetadataUpdateListener listener) + { + mIChannelMetadataUpdateListener = listener; + } + + /** + * Deregisters the listener from receiving field update events + */ + public void removeUpdateEventListener() + { + mIChannelMetadataUpdateListener = null; + } + + /** + * Broadcasts this metadata and the field that has been updated + * + * @param field that has been updated + */ + private void broadcastUpdate(ChannelMetadataField field) + { + if(mIChannelMetadataUpdateListener != null) + { + mIChannelMetadataUpdateListener.updated(this, field); + } + } + + @Override + public void receive(IdentifierUpdateNotification update) + { + Identifier identifier = update.getIdentifier(); + + switch(identifier.getIdentifierClass()) + { + case CONFIGURATION: + switch(identifier.getForm()) + { + case ALIAS_LIST: + if(update.isAdded()) + { + mAliasList = mAliasModel.getAliasList((AliasListConfigurationIdentifier)identifier); + + if(mAliasList != null) + { + if(mToIdentifier != null) + { + mToIdentifierAlias = mAliasList.getAlias(mToIdentifier); + } + if(mFromIdentifier != null) + { + mFromIdentifierAlias = mAliasList.getAlias(mFromIdentifier); + } + } + } + break; + case CHANNEL: + mChannelNameConfigurationIdentifier = update.isAdded() ? (ChannelNameConfigurationIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.CONFIGURATION_CHANNEL); + break; + case CHANNEL_FREQUENCY: + mFrequencyConfigurationIdentifier = update.isAdded() ? (FrequencyConfigurationIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.CONFIGURATION_FREQUENCY); + break; + case DECODER_TYPE: + mDecoderTypeConfigurationIdentifier = update.isAdded() ? (DecoderTypeConfigurationIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.DECODER_TYPE); + break; + case SITE: + mSiteConfigurationIdentifier = update.isAdded() ? (SiteConfigurationIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.CONFIGURATION_SITE); + break; + case SYSTEM: + mSystemConfigurationIdentifier = update.isAdded() ? (SystemConfigurationIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.CONFIGURATION_SYSTEM); + break; + } + break; + case DECODER: + switch(identifier.getForm()) + { + case CHANNEL_NAME: + mDecoderLogicalChannelNameIdentifier = update.isAdded() ? (DecoderLogicalChannelNameIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.DECODER_CHANNEL_NAME); + break; + case STATE: + mDecoderStateIdentifier = update.isAdded() ? (DecoderStateIdentifier)identifier : null; + broadcastUpdate(ChannelMetadataField.DECODER_STATE); + break; + } + break; + case USER: + if(identifier.getRole() == Role.FROM) + { + mFromIdentifier = update.isAdded() ? identifier : null; + + if(mAliasList != null && mFromIdentifier != null) + { + mFromIdentifierAlias = mAliasList.getAlias(mFromIdentifier); + } + else + { + mFromIdentifierAlias = null; + } + + broadcastUpdate(ChannelMetadataField.USER_FROM); + } + else if(identifier.getRole() == Role.TO) + { + mToIdentifier = update.isAdded() ? identifier : null; + + if(mAliasList != null && mToIdentifier != null) + { + mToIdentifierAlias = mAliasList.getAlias(mToIdentifier); + } + else + { + mToIdentifierAlias = null; + } + + broadcastUpdate(ChannelMetadataField.USER_TO); + } + break; + } + } +} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataField.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataField.java new file mode 100644 index 00000000..c68e800f --- /dev/null +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataField.java @@ -0,0 +1,34 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.channel.metadata; + +public enum ChannelMetadataField +{ + CONFIGURATION_CHANNEL, + CONFIGURATION_FREQUENCY, + CONFIGURATION_SITE, + CONFIGURATION_SYSTEM, + DECODER_CHANNEL_NAME, + DECODER_STATE, + DECODER_TYPE, + USER_FROM, + USER_TO; +} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java index 6e400936..1be1ccef 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java @@ -15,44 +15,65 @@ ******************************************************************************/ package io.github.dsheirer.channel.metadata; -import io.github.dsheirer.channel.state.State; +import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.alias.Alias; import io.github.dsheirer.controller.channel.Channel; -import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.preference.PreferenceType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.table.AbstractTableModel; import java.awt.EventQueue; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -public class ChannelMetadataModel extends AbstractTableModel implements Listener +public class ChannelMetadataModel extends AbstractTableModel implements IChannelMetadataUpdateListener { private final static Logger mLog = LoggerFactory.getLogger(ChannelMetadataModel.class); - private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.00000" ); + public static final int COLUMN_DECODER_STATE = 0; + public static final int COLUMN_DECODER_TYPE = 1; + public static final int COLUMN_USER_FROM = 2; + public static final int COLUMN_USER_FROM_ALIAS = 3; + public static final int COLUMN_USER_TO = 4; + public static final int COLUMN_USER_TO_ALIAS = 5; + public static final int COLUMN_DECODER_LOGICAL_CHANNEL_NAME = 6; + public static final int COLUMN_CONFIGURATION_FREQUENCY = 7; + public static final int COLUMN_CONFIGURATION_CHANNEL = 8; - public static final int COLUMN_STATE = 0; - public static final int COLUMN_DECODER = 1; - public static final int COLUMN_CHANNEL = 2; - public static final int COLUMN_FREQUENCY = 3; - public static final int COLUMN_PRIMARY_FROM = 4; - public static final int COLUMN_PRIMARY_TO = 5; - public static final int COLUMN_SECONDARY_FROM = 6; - public static final int COLUMN_SECONDARY_TO = 7; - public static final int COLUMN_CONFIGURATION_NAME = 8; - public static final int COLUMN_MESSAGE = 9; + private static final String[] COLUMNS = {"Status", "Decoder", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Channel Name"}; - private static final String[] COLUMNS = {"Status", "Decoder", "Channel", "Frequency", "Primary From", "Primary To", - "Secondary From", "Secondary To", "Channel Name", "Message"}; + private List mChannelMetadata = new ArrayList(); + private Map mMetadataChannelMap = new HashMap(); - private List mChannelMetadata = new ArrayList(); - private Map mMetadataChannelMap = new HashMap(); + public ChannelMetadataModel() + { + MyEventBus.getEventBus().register(this); + } - public void add(MutableMetadata metadata, Channel channel) + /** + * Receives preference update notifications via the event bus + * @param preferenceType that was updated + */ + @Subscribe + public void preferenceUpdated(PreferenceType preferenceType) + { + if(preferenceType == PreferenceType.IDENTIFIER) + { + for(int row = 0; row < mChannelMetadata.size(); row++) + { + fireTableCellUpdated(row, COLUMN_USER_FROM); + fireTableCellUpdated(row, COLUMN_USER_TO); + } + } + } + + + public void add(ChannelMetadata channelMetadata, Channel channel) { //Execute on the swing thread to avoid threading issues EventQueue.invokeLater(new Runnable() @@ -60,19 +81,16 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener @Override public void run() { - mChannelMetadata.add(metadata); - mMetadataChannelMap.put(metadata, channel); - - int index = mChannelMetadata.indexOf(metadata); - + mChannelMetadata.add(channelMetadata); + mMetadataChannelMap.put(channelMetadata, channel); + int index = mChannelMetadata.indexOf(channelMetadata); fireTableRowsInserted(index, index); - - metadata.addListener(ChannelMetadataModel.this); + channelMetadata.setUpdateEventListener(ChannelMetadataModel.this); } }); } - public void remove(MutableMetadata metadata) + public void remove(ChannelMetadata channelMetadata) { //Execute on the swing thread to avoid threading issues EventQueue.invokeLater(new Runnable() @@ -80,13 +98,10 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener @Override public void run() { - metadata.removeListener(ChannelMetadataModel.this); - - int index = mChannelMetadata.indexOf(metadata); - - mChannelMetadata.remove(metadata); - mMetadataChannelMap.remove(metadata); - + channelMetadata.removeUpdateEventListener(); + int index = mChannelMetadata.indexOf(channelMetadata); + mChannelMetadata.remove(channelMetadata); + mMetadataChannelMap.remove(channelMetadata); fireTableRowsDeleted(index, index); } }); @@ -95,7 +110,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener /** * Get the channel metadata at the specified model row index */ - public Metadata getMetadata(int row) + public ChannelMetadata getChannelMetadata(int row) { if(row < mChannelMetadata.size()) { @@ -108,9 +123,9 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener /** * Returns the channel that matches the metadata or null */ - public Channel getChannelFromMetadata(Metadata metadata) + public Channel getChannelFromMetadata(ChannelMetadata channelMetadata) { - return mMetadataChannelMap.get(metadata); + return mMetadataChannelMap.get(channelMetadata); } @Override @@ -136,24 +151,14 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener { switch(columnIndex) { - case COLUMN_STATE: - return State.class; - - case COLUMN_DECODER: - case COLUMN_CHANNEL: - case COLUMN_FREQUENCY: - case COLUMN_MESSAGE: - case COLUMN_CONFIGURATION_NAME: - return String.class; - - case COLUMN_PRIMARY_TO: - case COLUMN_PRIMARY_FROM: - case COLUMN_SECONDARY_TO: - case COLUMN_SECONDARY_FROM: - return MutableMetadata.class; - + case COLUMN_USER_FROM: + case COLUMN_USER_TO: + return ChannelMetadata.class; + case COLUMN_USER_FROM_ALIAS: + case COLUMN_USER_TO_ALIAS: + return Alias.class; default: - return String.class; + return Identifier.class; } } @@ -162,60 +167,28 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener { if(rowIndex <= mChannelMetadata.size()) { - Metadata metadata = mChannelMetadata.get(rowIndex); + ChannelMetadata channelMetadata = mChannelMetadata.get(rowIndex); switch(columnIndex) { - case COLUMN_STATE: - return metadata.getState(); - case COLUMN_DECODER: - if(metadata.hasPrimaryDecoderType()) - { - return metadata.getPrimaryDecoderType().getShortDisplayString(); - } - - return null; - case COLUMN_CHANNEL: - return metadata.getChannelFrequencyLabel(); - case COLUMN_FREQUENCY: - if(metadata.hasChannelFrequency()) - { - return FREQUENCY_FORMATTER.format((double)metadata.getChannelFrequency() / 1E6d); - } - - return null; - case COLUMN_MESSAGE: - if(metadata.isBufferOverflow()) - { - return "**OVERFLOW**"; - } - else if(metadata.hasMessageType() || metadata.hasMessage()) - { - StringBuilder sb = new StringBuilder(); - if(metadata.hasMessageType()) - { - sb.append(metadata.getMessageType()).append(" "); - } - - if(metadata.hasMessage()) - { - sb.append(metadata.getMessage()); - } - - return sb.toString(); - } - - return null; - case COLUMN_CONFIGURATION_NAME: - return metadata.getChannelConfigurationName(); - case COLUMN_PRIMARY_TO: - case COLUMN_PRIMARY_FROM: - case COLUMN_SECONDARY_TO: - case COLUMN_SECONDARY_FROM: - return metadata; - - default: - return String.class; + case COLUMN_DECODER_STATE: + return channelMetadata.getDecoderStateIdentifier(); + case COLUMN_DECODER_TYPE: + return channelMetadata.getDecoderTypeConfigurationIdentifier(); + case COLUMN_DECODER_LOGICAL_CHANNEL_NAME: + return channelMetadata.getDecoderLogicalChannelNameIdentifier(); + case COLUMN_CONFIGURATION_FREQUENCY: + return channelMetadata.getFrequencyConfigurationIdentifier(); + case COLUMN_CONFIGURATION_CHANNEL: + return channelMetadata.getChannelNameConfigurationIdentifier(); + case COLUMN_USER_TO: + return channelMetadata; + case COLUMN_USER_FROM: + return channelMetadata; + case COLUMN_USER_FROM_ALIAS: + return channelMetadata.getFromIdentifierAlias(); + case COLUMN_USER_TO_ALIAS: + return channelMetadata.getToIdentifierAlias(); } } @@ -223,59 +196,46 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener } @Override - public void receive(MutableMetadataChangeEvent mutableMetadataChangeEvent) + public void updated(ChannelMetadata channelMetadata, ChannelMetadataField channelMetadataField) { - //Execute on the swing thread to avoid threading issues - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - int rowIndex = mChannelMetadata.indexOf(mutableMetadataChangeEvent.getMetadata()); + final int rowIndex = mChannelMetadata.indexOf(channelMetadata); - if(rowIndex >= 0) + if(rowIndex >= 0) + { + //Execute on the swing thread to avoid threading issues + EventQueue.invokeLater(new Runnable() + { + @Override + public void run() { - switch(mutableMetadataChangeEvent.getAttribute()) + switch(channelMetadataField) { - case CHANNEL_CONFIGURATION_SYSTEM: + case CONFIGURATION_CHANNEL: + fireTableCellUpdated(rowIndex, COLUMN_CONFIGURATION_CHANNEL); break; - case CHANNEL_CONFIGURATION_SITE: + case CONFIGURATION_FREQUENCY: + fireTableCellUpdated(rowIndex, COLUMN_CONFIGURATION_FREQUENCY); break; - case CHANNEL_CONFIGURATION_NAME: - fireTableCellUpdated(rowIndex, COLUMN_CONFIGURATION_NAME); + case DECODER_CHANNEL_NAME: + fireTableCellUpdated(rowIndex, COLUMN_DECODER_LOGICAL_CHANNEL_NAME); break; - case CHANNEL_FREQUENCY: - fireTableCellUpdated(rowIndex, COLUMN_FREQUENCY); + case DECODER_TYPE: + fireTableCellUpdated(rowIndex, COLUMN_DECODER_TYPE); break; - case CHANNEL_FREQUENCY_LABEL: - fireTableCellUpdated(rowIndex, COLUMN_CHANNEL); + case DECODER_STATE: + fireTableCellUpdated(rowIndex, COLUMN_DECODER_STATE); break; - case CHANNEL_STATE: - fireTableCellUpdated(rowIndex, COLUMN_STATE); + case USER_FROM: + fireTableCellUpdated(rowIndex, COLUMN_USER_FROM); + fireTableCellUpdated(rowIndex, COLUMN_USER_FROM_ALIAS); break; - case BUFFER_OVERFLOW: - case MESSAGE: - case MESSAGE_TYPE: - fireTableCellUpdated(rowIndex, COLUMN_MESSAGE); - break; - case PRIMARY_ADDRESS_FROM: - fireTableCellUpdated(rowIndex, COLUMN_PRIMARY_FROM); - break; - case PRIMARY_ADDRESS_TO: - fireTableCellUpdated(rowIndex, COLUMN_PRIMARY_TO); - break; - case PRIMARY_DECODER_TYPE: - fireTableCellUpdated(rowIndex, COLUMN_DECODER); - break; - case SECONDARY_ADDRESS_FROM: - fireTableCellUpdated(rowIndex, COLUMN_SECONDARY_FROM); - break; - case SECONDARY_ADDRESS_TO: - fireTableCellUpdated(rowIndex, COLUMN_SECONDARY_TO); + case USER_TO: + fireTableCellUpdated(rowIndex, COLUMN_USER_TO); + fireTableCellUpdated(rowIndex, COLUMN_USER_TO_ALIAS); break; } } - } - }); + }); + } } } diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java index 1159360e..75f1cc28 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,21 +15,26 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.channel.metadata; import io.github.dsheirer.alias.Alias; import io.github.dsheirer.channel.state.State; import io.github.dsheirer.controller.channel.Channel; +import io.github.dsheirer.controller.channel.ChannelModel; import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.controller.channel.ChannelUtils; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier; +import io.github.dsheirer.identifier.decoder.DecoderStateIdentifier; import io.github.dsheirer.module.ProcessingChain; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import net.miginfocom.swing.MigLayout; -import org.apache.commons.lang3.Validate; import javax.swing.JLabel; import javax.swing.JMenuItem; @@ -44,13 +50,16 @@ import java.awt.Color; import java.awt.Component; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.text.DecimalFormat; import java.util.HashMap; import java.util.Map; public class ChannelMetadataPanel extends JPanel implements ListSelectionListener { + private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; private IconManager mIconManager; + private UserPreferences mUserPreferences; private JTable mTable; private Broadcaster mSelectedProcessingChainBroadcaster = new Broadcaster<>(); private Map mBackgroundColors = new HashMap<>(); @@ -59,11 +68,13 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene /** * Table view for currently decoding channel metadata */ - public ChannelMetadataPanel(ChannelProcessingManager channelProcessingManager, IconManager iconManager) + public ChannelMetadataPanel(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, + IconManager iconManager, UserPreferences userPreferences) { - mIconManager = iconManager; + mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; - + mIconManager = iconManager; + mUserPreferences = userPreferences; init(); } @@ -82,16 +93,18 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene mTable.getSelectionModel().addListSelectionListener(this); mTable.addMouseListener(new MouseSupport()); - mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_STATE) + mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_DECODER_STATE) .setCellRenderer(new ColoredStateCellRenderer()); - mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_PRIMARY_TO) - .setCellRenderer(new AliasedValueCellRenderer(Attribute.PRIMARY_ADDRESS_TO)); - mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_PRIMARY_FROM) - .setCellRenderer(new AliasedValueCellRenderer(Attribute.PRIMARY_ADDRESS_FROM)); - mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_SECONDARY_TO) - .setCellRenderer(new AliasedValueCellRenderer(Attribute.SECONDARY_ADDRESS_TO)); - mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_SECONDARY_FROM) - .setCellRenderer(new AliasedValueCellRenderer(Attribute.SECONDARY_ADDRESS_FROM)); + mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_USER_FROM) + .setCellRenderer(new FromCellRenderer(mUserPreferences.getTalkgroupFormatPreference())); + mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_USER_TO) + .setCellRenderer(new ToCellRenderer(mUserPreferences.getTalkgroupFormatPreference())); + mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_USER_FROM_ALIAS) + .setCellRenderer(new AliasCellRenderer()); + mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_USER_TO_ALIAS) + .setCellRenderer(new AliasCellRenderer()); + mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_CONFIGURATION_FREQUENCY) + .setCellRenderer(new FrequencyCellRenderer()); JScrollPane scrollPane = new JScrollPane(mTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); @@ -139,14 +152,16 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene { int selectedModelRow = mTable.convertRowIndexToModel(selectedViewRow); - Metadata selectedMetadata = mChannelProcessingManager.getChannelMetadataModel() - .getMetadata(selectedModelRow); + ChannelMetadata selectedMetadata = mChannelProcessingManager.getChannelMetadataModel() + .getChannelMetadata(selectedModelRow); if(selectedMetadata != null) { Channel selectedChannel = mChannelProcessingManager.getChannelMetadataModel() .getChannelFromMetadata(selectedMetadata); + + processingChain = mChannelProcessingManager.getProcessingChain(selectedChannel); } } @@ -164,35 +179,148 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene mSelectedProcessingChainBroadcaster.addListener(listener); } - public class AliasedValueCellRenderer extends DefaultTableCellRenderer + /** + * Cell renderer for frequency values + */ + public class FrequencyCellRenderer extends DefaultTableCellRenderer { - private Attribute mAttribute; + private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.00000" ); - public AliasedValueCellRenderer(Attribute attribute) + public FrequencyCellRenderer() { - Validate.isTrue(attribute != null); - mAttribute = attribute; setHorizontalAlignment(JLabel.CENTER); } @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, - int row, int column) + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Metadata metadata = (Metadata)value; - String value1 = metadata.getValue(mAttribute); - Alias alias = metadata.getAlias(mAttribute); - - label.setText(alias != null ? alias.getName() : value1); - label.setIcon(alias != null ? mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE) : null); - label.setForeground(alias != null ? alias.getDisplayColor() : table.getForeground()); + if(value instanceof FrequencyConfigurationIdentifier) + { + long frequency = ((FrequencyConfigurationIdentifier)value).getValue(); + label.setText(FREQUENCY_FORMATTER.format(frequency / 1e6d)); + } + else + { + label.setText(null); + } return label; } } + /** + * Alias cell renderer + */ + public class AliasCellRenderer extends DefaultTableCellRenderer + { + public AliasCellRenderer() + { + setHorizontalAlignment(JLabel.LEFT); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if(value instanceof Alias) + { + Alias alias = (Alias)value; + + label.setText(alias.getName()); + label.setIcon(mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE)); + label.setForeground(alias.getDisplayColor()); + + } + else + { + label.setText(null); + label.setIcon(null); + label.setForeground(table.getForeground()); + } + + return label; + } + } + + /** + * Abstract cell renderer for identifiers + */ + public abstract class IdentifierCellRenderer extends DefaultTableCellRenderer + { + private final static String EMPTY_VALUE = "-----"; + private TalkgroupFormatPreference mTalkgroupFormatPreference; + + public IdentifierCellRenderer(TalkgroupFormatPreference talkgroupFormatPreference) + { + mTalkgroupFormatPreference = talkgroupFormatPreference; + setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if(value instanceof ChannelMetadata) + { + ChannelMetadata channelMetadata = (ChannelMetadata)value; + Identifier identifier = getIdentifier(channelMetadata); + String text = mTalkgroupFormatPreference.format(identifier); + if(text == null || text.isEmpty()) + { + text = EMPTY_VALUE; + } + + label.setText(text); + } + else + { + label.setText(EMPTY_VALUE); + } + + return label; + } + + public abstract Identifier getIdentifier(ChannelMetadata channelMetadata); + } + + /** + * Cell renderer for the FROM identifier + */ + public class FromCellRenderer extends IdentifierCellRenderer + { + public FromCellRenderer(TalkgroupFormatPreference talkgroupFormatPreference) + { + super(talkgroupFormatPreference); + } + + @Override + public Identifier getIdentifier(ChannelMetadata channelMetadata) + { + return channelMetadata.getFromIdentifier(); + } + } + + /** + * Cell renderer for the TO identifier + */ + public class ToCellRenderer extends IdentifierCellRenderer + { + public ToCellRenderer(TalkgroupFormatPreference talkgroupFormatPreference) + { + super(talkgroupFormatPreference); + } + + @Override + public Identifier getIdentifier(ChannelMetadata channelMetadata) + { + return channelMetadata.getToIdentifier(); + } + } + public class ColoredStateCellRenderer extends DefaultTableCellRenderer { public ColoredStateCellRenderer() @@ -209,9 +337,9 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene Color background = table.getBackground(); Color foreground = table.getForeground(); - if(value instanceof State) + if(value instanceof DecoderStateIdentifier) { - State state = (State)value; + State state = ((DecoderStateIdentifier)value).getValue(); label.setText(state.getDisplayValue()); if(mBackgroundColors.containsKey(state)) @@ -226,7 +354,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene } else { - setText(""); + setText("----"); } setBackground(background); @@ -255,7 +383,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene if(modelRowIndex >= 0) { - Metadata metadata = mChannelProcessingManager.getChannelMetadataModel().getMetadata(modelRowIndex); + ChannelMetadata metadata = mChannelProcessingManager.getChannelMetadataModel().getChannelMetadata(modelRowIndex); if(metadata != null) { @@ -264,9 +392,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene if(channel != null) { - popupMenu.add(ChannelUtils.getContextMenu(mChannelProcessingManager.getChannelModel(), - mChannelProcessingManager, channel, mTable)); - + popupMenu.add(ChannelUtils.getContextMenu(mChannelModel, mChannelProcessingManager, channel, mTable)); populated = true; } } diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java index d0f3fd23..b7b3c65c 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java @@ -20,12 +20,14 @@ package io.github.dsheirer.channel.metadata; import com.jidesoft.swing.JideSplitPane; import com.jidesoft.swing.JideTabbedPane; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.channel.details.ChannelDetailPanel; +import io.github.dsheirer.controller.channel.ChannelModel; import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.module.decode.event.CallEventPanel; +import io.github.dsheirer.module.decode.event.DecodeEventPanel; import io.github.dsheirer.module.decode.event.MessageActivityPanel; -import io.github.dsheirer.settings.SettingsManager; +import io.github.dsheirer.preference.UserPreferences; import net.miginfocom.swing.MigLayout; import javax.swing.JPanel; @@ -36,20 +38,20 @@ public class ChannelMetadataViewer extends JPanel private ChannelMetadataPanel mChannelMetadataPanel; private ChannelDetailPanel mChannelDetailPanel; - private CallEventPanel mCallEventPanel; + private DecodeEventPanel mDecodeEventPanel; private MessageActivityPanel mMessageActivityPanel; /** * GUI panel that combines the currently decoding channels metadata table and viewers for channel details, * messages, events, and spectral view. */ - public ChannelMetadataViewer(ChannelProcessingManager channelProcessingManager, IconManager iconManager, - SettingsManager settingsManager) + public ChannelMetadataViewer(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, + IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences) { mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager); - mCallEventPanel = new CallEventPanel(iconManager); + mDecodeEventPanel = new DecodeEventPanel(iconManager, userPreferences, aliasModel); mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager); - mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager); + mChannelMetadataPanel = new ChannelMetadataPanel(channelModel, channelProcessingManager, iconManager, userPreferences); init(); } @@ -60,7 +62,7 @@ public class ChannelMetadataViewer extends JPanel JideTabbedPane tabbedPane = new JideTabbedPane(); tabbedPane.addTab("Details", mChannelDetailPanel); - tabbedPane.addTab("Events", mCallEventPanel); + tabbedPane.addTab("Events", mDecodeEventPanel); tabbedPane.addTab("Messages", mMessageActivityPanel); // tabbedPane.addTab("Spectrum", mChannelSpectrumPanel); tabbedPane.setFont(this.getFont()); @@ -73,7 +75,7 @@ public class ChannelMetadataViewer extends JPanel add(splitPane); mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelDetailPanel); - mChannelMetadataPanel.addProcessingChainSelectionListener(mCallEventPanel); + mChannelMetadataPanel.addProcessingChainSelectionListener(mDecodeEventPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel); } } diff --git a/src/main/java/io/github/dsheirer/channel/metadata/IChannelMetadataUpdateListener.java b/src/main/java/io/github/dsheirer/channel/metadata/IChannelMetadataUpdateListener.java new file mode 100644 index 00000000..1e0f8df1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/channel/metadata/IChannelMetadataUpdateListener.java @@ -0,0 +1,29 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.channel.metadata; + +/** + * Interface to receive channel metadata update notifications + */ +public interface IChannelMetadataUpdateListener +{ + void updated(ChannelMetadata channelMetadata, ChannelMetadataField channelMetadataField); +} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/Metadata.java b/src/main/java/io/github/dsheirer/channel/metadata/Metadata.java deleted file mode 100644 index 80bffc1a..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/Metadata.java +++ /dev/null @@ -1,664 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; -import io.github.dsheirer.alias.id.priority.Priority; -import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.module.decode.DecoderType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Set; -import java.util.TreeSet; - -public class Metadata -{ - private final static Logger mLog = LoggerFactory.getLogger(Metadata.class); - - // Static unique metadata identifier tracking - private static int UNIQUE_METADATA_ID_GENERATOR = 0; - - private int mMetadataID; - - protected boolean mUpdated; - protected DecoderType mPrimaryDecoderType; - protected boolean mSelected; - protected boolean mBufferOverflow; - protected State mState = State.IDLE; - protected String mChannelConfigurationSystem; - protected String mChannelConfigurationSite; - protected String mChannelConfigurationName; - protected String mChannelFrequencyLabel; - protected long mChannelFrequency; - protected AliasedIdentifier mNetworkID1 = new AliasedIdentifier(); - protected AliasedIdentifier mNetworkID2 = new AliasedIdentifier(); - - protected String mMessage; - protected String mMessageType; - protected AliasedIdentifier mPrimaryAddressFrom = new AliasedIdentifier(); - protected AliasedIdentifier mPrimaryAddressTo = new AliasedIdentifier(); - protected AliasedIdentifier mSecondaryAddressFrom = new AliasedIdentifier(); - protected AliasedIdentifier mSecondaryAddressTo = new AliasedIdentifier(); - - //Lazily constructed member variables. - private Integer mAudioPriority; - protected Boolean mRecordable = false; - protected Boolean mDoNotRecord; - private Set mBroadcastChannels; - - private long mTimestamp = System.currentTimeMillis(); - - - /** - * Channel metadata. Contains all attributes that reflect the state and current attribute values for a channel - * that is currently decoding. This metadata is intended to support any decoding channel gui components to - * graphically convey the current state of a decoding channel and to provide audio metadata - */ - public Metadata() - { - mMetadataID = UNIQUE_METADATA_ID_GENERATOR++; - } - - /** - * Protected constructor. Primarily used by the copyOf() method to create a metadata copy with the same ID - */ - protected Metadata(int metadataID) - { - mMetadataID = metadataID; - } - - /** - * Timestamp when this metadata was created - * @return milliseconds since epoch - */ - public long getTimestamp() - { - return mTimestamp; - } - /** - * Unique string identifier for this metadata that is comprised of the channel ID and the primary TO address. - * - * This identifier can be used to uniquely identify a channel audio stream and the primary communicant. - */ - public String getUniqueIdentifier() - { - return "SRC:" + mMetadataID + - " ID:" + (mPrimaryAddressTo.hasIdentifier() ? mPrimaryAddressTo.getIdentifier() : "UNKNOWN"); - } - - /** - * Indicates if any of the fields of this metadata have been updated since the last time a copy was made from this - * metadata. This method is primarily used by downstream audio playback and audio recording to signal when changes - * are made to the metadata that requires the downstream component to reinspect the metadata. - * - * This flag is reset to false immediately after a copy is made of this metadata via the copyOf() method. - */ - public boolean isUpdated() - { - return mUpdated; - } - - /** - * Audio Priority as the highest audio priority value from across the primary and secondary identifier aliases. - * - * A default priority will be used if there are no aliased values. If there is a conflict between audio priority - * and 'Do Not Monitor' among the aliases, the highest audio priority will be used (prefer to monitor). - * - * -1 Do Not Monitor - * 0 Channel Selected for immediate monitor - * 1 Highest Priority - * 100 Lowest Priority (default) - * - * @return audio priority - */ - public int getAudioPriority() - { - if(isSelected()) - { - return Priority.SELECTED_PRIORITY; - } - - if(mAudioPriority == null) - { - determineAudioPriority(); - } - - return mAudioPriority; - } - - /** - * Indicates if the audio priority is set to 'Do Not Monitor' - */ - public boolean isDoNotMonitor() - { - return getAudioPriority() == Priority.DO_NOT_MONITOR; - } - - /** - * Indicates if any of the primary or secondary TO/FROM aliases are identified as recordable - */ - public boolean isRecordable() - { - if(mDoNotRecord == null) - { - determineRecordable(); - } - - return mRecordable && !mDoNotRecord; - } - - /** - * Indicates that this metadata has been selected by the user and any audio produced by this channel will be set - * to the highest playback priority (immediate monitor). - */ - public boolean isSelected() - { - return mSelected; - } - - /** - * Indicates if the channel for this metadata is in a buffer overflow state. - */ - public boolean isBufferOverflow() - { - return mBufferOverflow; - } - - /** - * List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases. - */ - public Set getBroadcastChannels() - { - if(mBroadcastChannels == null) - { - determineBroadcastChannels(); - } - - return mBroadcastChannels; - } - - /** - * Indicates if this metadata is streamable, meaning that it contains one ore more broadcast channels - */ - public boolean isStreamable() - { - return !getBroadcastChannels().isEmpty(); - } - - /** - * Unique identifier for the source channel that produces this metadata - */ - public int getMetadataID() - { - return mMetadataID; - } - - /** - * Decoder type (ie protocol) for the primary decoder - */ - public DecoderType getPrimaryDecoderType() - { - return mPrimaryDecoderType; - } - - /** - * Indicates if this metadata has a primary decoder type defined - */ - public boolean hasPrimaryDecoderType() - { - return mPrimaryDecoderType != null; - } - - /** - * Returns the alias associated with the attribute or null - */ - public Alias getAlias(Attribute attribute) - { - switch(attribute) - { - case NETWORK_ID_1: - return getNetworkID1().getAlias(); - case NETWORK_ID_2: - return getNetworkID2().getAlias(); - case PRIMARY_ADDRESS_FROM: - return getPrimaryAddressFrom().getAlias(); - case PRIMARY_ADDRESS_TO: - return getPrimaryAddressTo().getAlias(); - case SECONDARY_ADDRESS_FROM: - return getSecondaryAddressFrom().getAlias(); - case SECONDARY_ADDRESS_TO: - return getSecondaryAddressTo().getAlias(); - } - - return null; - } - - /** - * Returns the string value for the specified attribute or null - */ - public String getValue(Attribute attribute) - { - switch(attribute) - { - case CHANNEL_CONFIGURATION_SYSTEM: - return mChannelConfigurationSystem; - case CHANNEL_CONFIGURATION_SITE: - return mChannelConfigurationSite; - case CHANNEL_FREQUENCY_LABEL: - return mChannelFrequencyLabel; - case CHANNEL_STATE: - return mState.getDisplayValue(); - case MESSAGE: - return mMessage; - case MESSAGE_TYPE: - return mMessageType; - case NETWORK_ID_1: - return mNetworkID1.getIdentifier(); - case NETWORK_ID_2: - return mNetworkID2.getIdentifier(); - case PRIMARY_ADDRESS_FROM: - return mPrimaryAddressFrom.getIdentifier(); - case PRIMARY_ADDRESS_TO: - return mPrimaryAddressTo.getIdentifier(); - case SECONDARY_ADDRESS_FROM: - return mSecondaryAddressFrom.getIdentifier(); - case SECONDARY_ADDRESS_TO: - return mSecondaryAddressTo.getIdentifier(); - } - - return null; - } - - /** - * Current channel state - */ - public State getState() - { - return mState; - } - - /** - * System name from the channel configuration - */ - public String getChannelConfigurationSystem() - { - return mChannelConfigurationSystem; - } - - /** - * Indicates if there is a non-null, non-empty channel configuration system value - */ - public boolean hasChannelConfigurationSystem() - { - return mChannelConfigurationSystem != null && !mChannelConfigurationSystem.isEmpty(); - } - - /** - * Site name from the channel configuration - */ - public String getChannelConfigurationSite() - { - return mChannelConfigurationSite; - } - - /** - * Indicates if there is a non-null, non-empty channel configuration site value - */ - public boolean hasChannelConfigurationSite() - { - return mChannelConfigurationSite != null && !mChannelConfigurationSite.isEmpty(); - } - - /** - * Site name from the channel configuration - */ - public String getChannelConfigurationName() - { - return mChannelConfigurationName; - } - - /** - * Indicates if there is a non-null, non-empty channel configuration name value - */ - public boolean hasChannelConfigurationName() - { - return mChannelConfigurationSite != null && !mChannelConfigurationSite.isEmpty(); - } - - /** - * Channel ID or Channel Number - */ - public String getChannelFrequencyLabel() - { - return mChannelFrequencyLabel; - } - - /** - * Indicates if there is a non-null, non-empty channel identifier value - */ - public boolean hasChannelFrequencyLabel() - { - return mChannelFrequencyLabel != null && !mChannelFrequencyLabel.isEmpty(); - } - - /** - * Channel frequency - */ - public long getChannelFrequency() - { - return mChannelFrequency; - } - - /** - * Indicates if there is a non-zero channel frequency value - */ - public boolean hasChannelFrequency() - { - return mChannelFrequency > 0; - } - - /** - * Network Identifier 1 - */ - public AliasedIdentifier getNetworkID1() - { - return mNetworkID1; - } - - /** - * Network Identifier 2 - */ - public AliasedIdentifier getNetworkID2() - { - return mNetworkID2; - } - - /** - * Message - */ - public String getMessage() - { - return mMessage; - } - - /** - * Indicates if there is a non-null, non-empty message value - */ - public boolean hasMessage() - { - return mMessage != null && !mMessage.isEmpty(); - } - - /** - * Message and Decoder type - */ - public String getMessageType() - { - return mMessageType; - } - - /** - * Indicates if there is a non-null, non-empty message and decoder type value - */ - public boolean hasMessageType() - { - return mMessageType != null && !mMessageType.isEmpty(); - } - - /** - * Primary FROM Address - */ - public AliasedIdentifier getPrimaryAddressFrom() - { - return mPrimaryAddressFrom; - } - - /** - * Primary TO Address - */ - public AliasedIdentifier getPrimaryAddressTo() - { - return mPrimaryAddressTo; - } - - /** - * Secondary FROM Address - */ - public AliasedIdentifier getSecondaryAddressFrom() - { - return mSecondaryAddressFrom; - } - - /** - * Secondary TO Address - */ - public AliasedIdentifier getSecondaryAddressTo() - { - return mSecondaryAddressTo; - } - - /** - * Determines the audio priority from across the primary and secondary TO/FROM aliases when they exist - */ - private void determineAudioPriority() - { - int priority = Priority.DEFAULT_PRIORITY; - - boolean doNotMonitorFound = false; - boolean audioPriorityFound = false; - - if(getPrimaryAddressTo().hasAlias()) - { - Alias primaryTo = getPrimaryAddressTo().getAlias(); - - if(primaryTo.hasCallPriority()) - { - if(primaryTo.getCallPriority() == Priority.DO_NOT_MONITOR) - { - doNotMonitorFound = true; - } - else - { - audioPriorityFound = true; - - if(primaryTo.getCallPriority() < priority) - { - priority = primaryTo.getCallPriority(); - } - } - } - } - - if(getPrimaryAddressFrom().hasAlias()) - { - Alias primaryFrom = getPrimaryAddressFrom().getAlias(); - - if(primaryFrom.hasCallPriority()) - { - if(primaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR) - { - doNotMonitorFound = true; - } - else - { - audioPriorityFound = true; - - if(primaryFrom.getCallPriority() < priority) - { - priority = primaryFrom.getCallPriority(); - } - } - } - } - - if(getSecondaryAddressTo().hasAlias()) - { - Alias secondaryTo = getSecondaryAddressTo().getAlias(); - - if(secondaryTo.hasCallPriority()) - { - if(secondaryTo.getCallPriority() == Priority.DO_NOT_MONITOR) - { - doNotMonitorFound = true; - } - else - { - audioPriorityFound = true; - - if(secondaryTo.getCallPriority() < priority) - { - priority = secondaryTo.getCallPriority(); - } - } - } - } - - if(getSecondaryAddressFrom().hasAlias()) - { - Alias secondaryFrom = getSecondaryAddressFrom().getAlias(); - - if(secondaryFrom.hasCallPriority()) - { - if(secondaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR) - { - doNotMonitorFound = true; - } - else - { - audioPriorityFound = true; - - if(secondaryFrom.getCallPriority() < priority) - { - priority = secondaryFrom.getCallPriority(); - } - } - } - } - - //If we found a 'Do Not Monitor' and no other audio priority, then set to do not monitor - if(doNotMonitorFound && !audioPriorityFound) - { - mAudioPriority = Priority.DO_NOT_MONITOR; - } - else - { - mAudioPriority = priority; - } - } - - /** - * Determines if any of the primary or secondary to/from aliases are recordable. - */ - private void determineRecordable() - { - mDoNotRecord = false; - - if(mPrimaryAddressTo.hasAlias() && !mPrimaryAddressTo.getAlias().isRecordable()) - { - mDoNotRecord = true; - return; - } - - if(mPrimaryAddressFrom.hasAlias() && !mPrimaryAddressFrom.getAlias().isRecordable()) - { - mDoNotRecord = true; - return; - } - - if(mSecondaryAddressTo.hasAlias() && !mSecondaryAddressTo.getAlias().isRecordable()) - { - mDoNotRecord = true; - return; - } - - if(mSecondaryAddressFrom.hasAlias() && !mSecondaryAddressFrom.getAlias().isRecordable()) - { - mDoNotRecord = true; - return; - } - } - - /** - * Aggregates a de-duplicated list of broadcast channels from among the primary/secondary to/from aliases - */ - private void determineBroadcastChannels() - { - mBroadcastChannels = new TreeSet<>(); - - if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isStreamable()) - { - mBroadcastChannels.addAll(mPrimaryAddressTo.getAlias().getBroadcastChannels()); - } - - if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isStreamable()) - { - mBroadcastChannels.addAll(mPrimaryAddressFrom.getAlias().getBroadcastChannels()); - } - - if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isStreamable()) - { - mBroadcastChannels.addAll(mSecondaryAddressTo.getAlias().getBroadcastChannels()); - } - - if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isStreamable()) - { - mBroadcastChannels.addAll(mSecondaryAddressFrom.getAlias().getBroadcastChannels()); - } - } - - /** - * Creates a deep/full snapshot copy of this metadata set. - * - * The streamable, recordable, audio priority and broadcast channels are not copied. These are lazily determined - * when initially requested. - */ - public Metadata copyOf() - { - Metadata copy = new Metadata(mMetadataID); - - copy.mDoNotRecord = mDoNotRecord; - copy.mRecordable = mRecordable; - copy.mState = mState; - copy.mPrimaryDecoderType = mPrimaryDecoderType; - copy.mChannelFrequency = mChannelFrequency; - - copy.mChannelConfigurationSystem = hasChannelConfigurationSystem() ? new String(mChannelConfigurationSystem) : null; - copy.mChannelConfigurationSite = hasChannelConfigurationSite() ? new String(mChannelConfigurationSite) : null; - copy.mChannelConfigurationName = hasChannelConfigurationName() ? new String(mChannelConfigurationName) : null; - copy.mChannelFrequencyLabel = hasChannelFrequencyLabel() ? new String(mChannelFrequencyLabel) : null; - copy.mMessage = hasMessage() ? new String(mMessage) : null; - copy.mMessageType = hasMessageType() ? new String(mMessageType) : null; - - copy.mNetworkID1 = mNetworkID1.copyOf(); - copy.mNetworkID2 = mNetworkID2.copyOf(); - copy.mPrimaryAddressFrom = mPrimaryAddressFrom.copyOf(); - copy.mPrimaryAddressTo = mPrimaryAddressTo.copyOf(); - copy.mSecondaryAddressFrom = mSecondaryAddressFrom.copyOf(); - copy.mSecondaryAddressTo = mSecondaryAddressTo.copyOf(); - - copy.mUpdated = mUpdated; - - //Reset the updated flag - mUpdated = false; - - return copy; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/MutableMetadata.java b/src/main/java/io/github/dsheirer/channel/metadata/MutableMetadata.java deleted file mode 100644 index df4e297d..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/MutableMetadata.java +++ /dev/null @@ -1,294 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.sample.Broadcaster; -import io.github.dsheirer.sample.Listener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MutableMetadata extends Metadata implements Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(MutableMetadata.class); - - private Broadcaster mMetadataChangeEventBroadcaster = new Broadcaster<>(); - - /** - * Mutable channel metadata. Contains all attributes that reflect the state and current attribute values for a - * channel that is currently decoding. This metadata is intended to support any decoding channel gui components to - * graphically convey the current state of a decoding channel and to provide audio metadata - */ - public MutableMetadata() - { - super(); - } - - /** - * Sets the selected state for this metadata. Selected indicates that the user has selected this metadata for - * immediate monitor in the audio panel. When true, this overrides the audio priority for any audio produced - * by the channel and sets that audio to the highest priority. - * - * Any component that toggles the selected state for this metadata is also responsible for clearing the flag. - */ - public void setSelected(boolean selected) - { - mSelected = selected; - mUpdated = true; - } - - /** - * Sets the recordable flag to the argument value and overrides any recordable settings in the enclosed aliases. - */ - public void setRecordable(boolean recordable) - { - mRecordable = recordable; - } - - /** - * Sets the primary decoder type - */ - public void setPrimaryDecoderType(DecoderType decoderType) - { - mPrimaryDecoderType = decoderType; - mUpdated = true; - } - - /** - * Request to change an attribute value for this metadata. This is the primary method for updating all attributes - * in this metadata set. - */ - @Override - public void receive(AttributeChangeRequest request) - { - switch(request.getAttribute()) - { - case BUFFER_OVERFLOW: - mBufferOverflow = request.getBooleanValue(); - broadcast(Attribute.BUFFER_OVERFLOW); - break; - case CHANNEL_CONFIGURATION_SYSTEM: - mChannelConfigurationSystem = request.getStringValue(); - broadcast(Attribute.CHANNEL_CONFIGURATION_SYSTEM); - break; - case CHANNEL_CONFIGURATION_SITE: - mChannelConfigurationSite = request.getStringValue(); - broadcast(Attribute.CHANNEL_CONFIGURATION_SITE); - break; - case CHANNEL_CONFIGURATION_NAME: - mChannelConfigurationName = request.getStringValue(); - broadcast(Attribute.CHANNEL_CONFIGURATION_NAME); - break; - case CHANNEL_FREQUENCY: - if(request.hasValue()) - { - mChannelFrequency = request.getLongValue(); - } - broadcast(Attribute.CHANNEL_FREQUENCY); - break; - case CHANNEL_FREQUENCY_LABEL: - mChannelFrequencyLabel = request.getStringValue(); - broadcast(Attribute.CHANNEL_FREQUENCY_LABEL); - break; - case MESSAGE: - mMessage = request.getStringValue(); - broadcast(Attribute.MESSAGE); - break; - case MESSAGE_TYPE: - mMessageType = request.getStringValue(); - broadcast(Attribute.MESSAGE_TYPE); - break; - case NETWORK_ID_1: - mNetworkID1.setIdentifier(request.getStringValue()); - mNetworkID1.setAlias(request.getAlias()); - broadcast(Attribute.NETWORK_ID_1); - break; - case NETWORK_ID_2: - mNetworkID2.setIdentifier(request.getStringValue()); - mNetworkID2.setAlias(request.getAlias()); - broadcast(Attribute.NETWORK_ID_2); - break; - case PRIMARY_ADDRESS_FROM: - mPrimaryAddressFrom.setIdentifier(request.getStringValue()); - mPrimaryAddressFrom.setAlias(request.getAlias()); - broadcast(Attribute.PRIMARY_ADDRESS_FROM); - break; - case PRIMARY_ADDRESS_TO: - mPrimaryAddressTo.setIdentifier(request.getStringValue()); - mPrimaryAddressTo.setAlias(request.getAlias()); - broadcast(Attribute.PRIMARY_ADDRESS_TO); - break; - case PRIMARY_DECODER_TYPE: - mPrimaryDecoderType = request.getDecoderTypeValue(); - broadcast(Attribute.PRIMARY_DECODER_TYPE); - break; - case SECONDARY_ADDRESS_FROM: - mSecondaryAddressFrom.setIdentifier(request.getStringValue()); - mSecondaryAddressFrom.setAlias(request.getAlias()); - broadcast(Attribute.SECONDARY_ADDRESS_FROM); - break; - case SECONDARY_ADDRESS_TO: - mSecondaryAddressTo.setIdentifier(request.getStringValue()); - mSecondaryAddressTo.setAlias(request.getAlias()); - broadcast(Attribute.SECONDARY_ADDRESS_TO); - break; - case CHANNEL_STATE: - mState = (State) request.getValue(); - broadcast(Attribute.CHANNEL_STATE); - break; - default: - throw new IllegalArgumentException("Unrecognized Metadata Attribute: " + - request.getAttribute().name()); - } - - mUpdated = true; - } - - /** - * Resets temporal attributes to null value. This only resets attributes that have a non-ull value in order to - * limit the number of change events that are produced. - */ - public void resetTemporalAttributes() - { - mDoNotRecord = null; - mBufferOverflow = false; - - if(hasMessage()) - { - mMessage = null; - broadcast(Attribute.MESSAGE); - } - - if(hasMessageType()) - { - mMessageType = null; - broadcast(Attribute.MESSAGE_TYPE); - } - - if(mPrimaryAddressFrom.hasIdentifier()) - { - mPrimaryAddressFrom.reset(); - broadcast(Attribute.PRIMARY_ADDRESS_FROM); - } - - if(mPrimaryAddressTo.hasIdentifier()) - { - mPrimaryAddressTo.reset(); - broadcast(Attribute.PRIMARY_ADDRESS_TO); - } - - if(mSecondaryAddressFrom.hasIdentifier()) - { - mSecondaryAddressFrom.reset(); - broadcast(Attribute.SECONDARY_ADDRESS_FROM); - } - - if(mSecondaryAddressTo.hasIdentifier()) - { - mSecondaryAddressTo.reset(); - broadcast(Attribute.SECONDARY_ADDRESS_TO); - } - - mUpdated = true; - } - - /** - * Resets all attributes to empty values - */ - public void resetAllAttributes() - { - resetTemporalAttributes(); - - if(hasChannelConfigurationSystem()) - { - mChannelConfigurationSystem = null; - broadcast(Attribute.CHANNEL_CONFIGURATION_SYSTEM); - } - - if(hasChannelConfigurationSite()) - { - mChannelConfigurationSite = null; - broadcast(Attribute.CHANNEL_CONFIGURATION_SITE); - } - - if(hasChannelConfigurationName()) - { - mChannelConfigurationSite = null; - broadcast(Attribute.CHANNEL_CONFIGURATION_NAME); - } - - if(hasChannelFrequencyLabel()) - { - mChannelFrequencyLabel = null; - broadcast(Attribute.CHANNEL_FREQUENCY_LABEL); - } - - if(hasChannelFrequency()) - { - mChannelFrequency = 0; - broadcast(Attribute.CHANNEL_FREQUENCY); - } - - if(hasPrimaryDecoderType()) - { - mPrimaryDecoderType = null; - broadcast(Attribute.PRIMARY_DECODER_TYPE); - } - - if(mNetworkID1.hasIdentifier()) - { - mNetworkID1.reset(); - broadcast(Attribute.NETWORK_ID_1); - } - - if(mNetworkID2.hasIdentifier()) - { - mNetworkID2.reset(); - broadcast(Attribute.NETWORK_ID_2); - } - - mUpdated = true; - } - - - /** - * Broadcasts to registered listeners that an attribute has changed for this metadata - */ - private void broadcast(Attribute attribute) - { - mMetadataChangeEventBroadcaster.broadcast(new MutableMetadataChangeEvent(this, attribute)); - } - - /** - * Adds the listener to receive metadata change events for this channel metadata - */ - public void addListener(Listener listener) - { - mMetadataChangeEventBroadcaster.addListener(listener); - } - - /** - * Removes the listener from receiving metadata change events for this channel metadata - */ - public void removeListener(Listener listener) - { - mMetadataChangeEventBroadcaster.removeListener(listener); - } -} diff --git a/src/main/java/io/github/dsheirer/channel/metadata/MutableMetadataChangeEvent.java b/src/main/java/io/github/dsheirer/channel/metadata/MutableMetadataChangeEvent.java deleted file mode 100644 index c083140d..00000000 --- a/src/main/java/io/github/dsheirer/channel/metadata/MutableMetadataChangeEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.metadata; - -public class MutableMetadataChangeEvent -{ - private MutableMetadata mMetadata; - private Attribute mAttribute; - - /** - * Channel metadata changed attribute event - * - * @param metadata where the change occurred - * @param attribute that changed - */ - public MutableMetadataChangeEvent(MutableMetadata metadata, Attribute attribute) - { - mMetadata = metadata; - mAttribute = attribute; - } - - /** - * Channel metadata where the change occurred - */ - public MutableMetadata getMetadata() - { - return mMetadata; - } - - /** - * Channel metadata attribute that has changed - */ - public Attribute getAttribute() - { - return mAttribute; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java b/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java index 1d3795e8..4d145b90 100644 --- a/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java +++ b/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java @@ -1,26 +1,26 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.channel.state; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,8 +39,6 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState public AlwaysUnsquelchedDecoderState(DecoderType decoderType, String channelName) { - super(null); - mDecoderType = decoderType; mChannelName = channelName; } @@ -69,9 +67,9 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState } @Override - public void receive(Message t) + public void receive(IMessage t) { - /* Not implemented */ + /* Not implemented */ } @Override @@ -81,7 +79,7 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState { //Each time we're reset, set the PRIMARY TO attribute back to the channel name, otherwise we won't have //a primary ID for any audio produced by this state. - broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, NO_SQUELCH)); +// broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, NO_SQUELCH)); } } @@ -94,7 +92,7 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState @Override public void start() { - broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, NO_SQUELCH)); +// broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, NO_SQUELCH)); broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE)); } diff --git a/src/main/java/io/github/dsheirer/channel/state/ChannelState.java b/src/main/java/io/github/dsheirer/channel/state/ChannelState.java index c688035b..ee0c3fda 100644 --- a/src/main/java/io/github/dsheirer/channel/state/ChannelState.java +++ b/src/main/java/io/github/dsheirer/channel/state/ChannelState.java @@ -18,51 +18,64 @@ ******************************************************************************/ package io.github.dsheirer.channel.state; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.squelch.ISquelchStateProvider; import io.github.dsheirer.audio.squelch.SquelchState; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; -import io.github.dsheirer.channel.metadata.IAttributeChangeRequestListener; -import io.github.dsheirer.channel.metadata.MutableMetadata; +import io.github.dsheirer.channel.metadata.ChannelMetadata; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; -import io.github.dsheirer.channel.traffic.TrafficChannelManager; +import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.Channel.ChannelType; +import io.github.dsheirer.controller.channel.ChannelEvent; +import io.github.dsheirer.controller.channel.IChannelEventProvider; +import io.github.dsheirer.identifier.IdentifierUpdateListener; +import io.github.dsheirer.identifier.IdentifierUpdateNotification; +import io.github.dsheirer.identifier.IdentifierUpdateProvider; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.ChannelNameConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.DecoderTypeConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier; +import io.github.dsheirer.identifier.decoder.DecoderStateIdentifier; import io.github.dsheirer.module.Module; -import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.config.DecodeConfiguration; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.event.ICallEventProvider; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEventProvider; import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.ISourceEventListener; import io.github.dsheirer.source.ISourceEventProvider; import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.SourceType; +import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.heartbeat.Heartbeat; import io.github.dsheirer.source.heartbeat.IHeartbeatListener; +import io.github.dsheirer.source.tuner.channel.TunerChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener, - IDecoderStateEventProvider, ISourceEventListener, ISourceEventProvider, IHeartbeatListener, - ISquelchStateProvider, IAttributeChangeRequestListener, IOverflowListener +public class ChannelState extends Module implements IChannelEventProvider, IDecodeEventProvider, + IDecoderStateEventListener, IDecoderStateEventProvider, ISourceEventListener, ISourceEventProvider, + IHeartbeatListener, ISquelchStateProvider, IOverflowListener, IdentifierUpdateListener, IdentifierUpdateProvider { private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class); public static final long FADE_TIMEOUT_DELAY = 1200; public static final long RESET_TIMEOUT_DELAY = 2000; - private MutableMetadata mMutableMetadata = new MutableMetadata(); private State mState = State.IDLE; - private Listener mCallEventListener; + private MutableIdentifierCollection mIdentifierCollection = new MutableIdentifierCollection(); + private Listener mChannelEventListener; + private Listener mDecodeEventListener; private Listener mDecoderStateListener; private Listener mSquelchStateListener; private Listener mExternalSourceEventListener; private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver(); private HeartbeatReceiver mHeartbeatReceiver = new HeartbeatReceiver(); - private ChannelType mChannelType; - private TrafficChannelManager mTrafficChannelEndListener; - private CallEvent mTrafficChannelCallEvent; + private Channel mChannel; private SourceEventListener mInternalSourceEventListener; + private ChannelMetadata mChannelMetadata; private boolean mSquelchLocked = false; private boolean mSelected = false; @@ -87,25 +100,85 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder * IDLE: Normal state. No voice or data call activity * CALL/DATA/ENCRYPTED/CONTROL: Decoding states. * FADE: The phase after a voice or data call when either an explicit call end has been received, or when no new - * signalling updates have been received, and the fade timer has expired. This phase allows for gui updates to - * signal to the user that the call is ended, while continuing to display the call details for the user + * signalling updates have been received, and the fade timer has expired. This phase allows for gui updates to + * signal to the user that the call is ended, while continuing to display the call details for the user * TEARDOWN: Indicates a traffic channel that will be torn down for reuse. */ - public ChannelState(ChannelType channelType) + public ChannelState(Channel channel, AliasModel aliasModel) { - mChannelType = channelType; + mChannel = channel; + mChannelMetadata = new ChannelMetadata(aliasModel); + createConfigurationIdentifiers(channel); } + /** + * Creates configuration identifiers for the channel name, system, site and alias list name. + */ + private void createConfigurationIdentifiers(Channel channel) + { + mIdentifierCollection.update(DecoderTypeConfigurationIdentifier.create(channel.getDecodeConfiguration().getDecoderType())); + + if(channel.hasSystem()) + { + mIdentifierCollection.update(SystemConfigurationIdentifier.create(channel.getSystem())); + } + if(channel.hasSite()) + { + mIdentifierCollection.update(SiteConfigurationIdentifier.create(channel.getSite())); + } + if(channel.getName() != null && !channel.getName().isEmpty()) + { + mIdentifierCollection.update(ChannelNameConfigurationIdentifier.create(channel.getName())); + } + if(channel.getAliasListName() != null && !channel.getAliasListName().isEmpty()) + { + mIdentifierCollection.update(AliasListConfigurationIdentifier.create(channel.getAliasListName())); + } + if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) + { + TunerChannel tunerChannel = ((SourceConfigTuner)channel.getSourceConfiguration()).getTunerChannel(); + mIdentifierCollection.update(FrequencyConfigurationIdentifier.create(tunerChannel.getFrequency())); + } + } /** - * Metadata for this channel containing channel details, primary and secondary entities for call events and any - * decoded messages. This metadata is primarily intended to be used by graphical components to convey the - * details of the current channel state and to provide metadata backing any decoded audio packets produced by - * any audio streams for this channel. + * Interface to receive channel identifier updates from this channel state and from any + * decoder states. */ - public MutableMetadata getMutableMetadata() + @Override + public Listener getIdentifierUpdateListener() { - return mMutableMetadata; + return mChannelMetadata; + } + + /** + * Updates the channel state identifier collection using the update notification. This update will be reflected + * in the internal channel state and will also be broadcast to any listeners, including the channel metadata for + * this channel state. + */ + public void updateChannelStateIdentifiers(IdentifierUpdateNotification notification) + { + mIdentifierCollection.receive(notification); + } + + @Override + public void setIdentifierUpdateListener(Listener listener) + { + mIdentifierCollection.setIdentifierUpdateListener(listener); + } + + @Override + public void removeIdentifierUpdateListener() + { + mIdentifierCollection.removeIdentifierUpdateListener(); + } + + /** + * Channel metadata for this channel. + */ + public ChannelMetadata getChannelMetadata() + { + return mChannelMetadata; } /** @@ -114,21 +187,18 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder @Override public void reset() { - getMutableMetadata().resetTemporalAttributes(); - - broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE)); - mState = State.IDLE; - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); + broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE)); + mIdentifierCollection.update(DecoderStateIdentifier.IDLE); mSourceOverflow = false; } @Override public void start() { - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); + mIdentifierCollection.broadcastIdentifiers(); - if(mTrafficChannelEndListener != null) + if(mChannel.getChannelType() == ChannelType.TRAFFIC) { setState(State.CALL); } @@ -143,7 +213,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder public void dispose() { - mCallEventListener = null; + mDecodeEventListener = null; mDecoderStateListener = null; mSquelchStateListener = null; } @@ -159,21 +229,11 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder return mInternalSourceEventListener; } - private boolean isStandardChannel() - { - return mChannelType == ChannelType.STANDARD; - } - - private boolean isTrafficChannel() - { - return mChannelType == ChannelType.TRAFFIC; - } - public void setStandardChannelTimeout(long milliseconds) { mStandardChannelFadeTimeout = milliseconds; - if(mChannelType == ChannelType.STANDARD) + if(mChannel.isStandardChannel()) { mFadeTimeout = mStandardChannelFadeTimeout; } @@ -183,7 +243,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder { mTrafficChannelFadeTimeout = milliseconds; - if(mChannelType == ChannelType.TRAFFIC) + if(mChannel.isTrafficChannel()) { mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout; } @@ -209,7 +269,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder */ private void updateFadeTimeout() { - if(isTrafficChannel()) + if(mChannel.isTrafficChannel()) { mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout; } @@ -225,7 +285,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder */ private void updateResetTimeout() { - if(isTrafficChannel()) + if(mChannel.isTrafficChannel()) { mEndTimeout = System.currentTimeMillis(); } @@ -298,26 +358,35 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder broadcast(SquelchState.SQUELCH); updateFadeTimeout(); mState = state; + mIdentifierCollection.update(DecoderStateIdentifier.ACTIVE); break; case CONTROL: //Don't allow traffic channels to be control channels, otherwise they can't transition to teardown - if(isStandardChannel()) + if(mChannel.isStandardChannel()) { broadcast(SquelchState.SQUELCH); updateFadeTimeout(); mState = state; + mIdentifierCollection.update(DecoderStateIdentifier.CONTROL); } break; case DATA: + broadcast(SquelchState.SQUELCH); + updateFadeTimeout(); + mState = state; + mIdentifierCollection.update(DecoderStateIdentifier.DATA); + break; case ENCRYPTED: broadcast(SquelchState.SQUELCH); updateFadeTimeout(); mState = state; + mIdentifierCollection.update(DecoderStateIdentifier.ENCRYPTED); break; case CALL: broadcast(SquelchState.UNSQUELCH); updateFadeTimeout(); mState = state; + mIdentifierCollection.update(DecoderStateIdentifier.CALL); break; case FADE: processFadeState(); @@ -330,12 +399,11 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder break; case RESET: mState = State.IDLE; + mIdentifierCollection.update(DecoderStateIdentifier.IDLE); break; default: break; } - - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); } } @@ -351,14 +419,13 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder /** * This method is invoked if the source buffer provider goes into overflow state. Since this is an external state, * we use the mSourceOverflow variable to override the internal state reported to external listeners. + * * @param overflow true to indicate an overflow state */ @Override public void sourceOverflow(boolean overflow) { mSourceOverflow = overflow; - - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.BUFFER_OVERFLOW, mSourceOverflow)); } /** @@ -380,9 +447,9 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder { updateResetTimeout(); mState = State.FADE; + mIdentifierCollection.update(DecoderStateIdentifier.FADE); broadcast(SquelchState.SQUELCH); - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); } private void processIdleState() @@ -391,12 +458,11 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder if(mState == State.FADE) { - getMutableMetadata().resetTemporalAttributes(); broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE)); } mState = State.IDLE; - getMutableMetadata().receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); + mIdentifierCollection.update(DecoderStateIdentifier.IDLE); } private void processTeardownState() @@ -404,48 +470,58 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder broadcast(SquelchState.SQUELCH); mState = State.TEARDOWN; + mIdentifierCollection.update(DecoderStateIdentifier.TEARDOWN); - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); - - if(mTrafficChannelEndListener != null && mTrafficChannelCallEvent != null) + if(mChannel.isTrafficChannel()) { - mTrafficChannelCallEvent.end(); - broadcast(mTrafficChannelCallEvent); - mTrafficChannelEndListener.callEnd(mTrafficChannelCallEvent.getChannel()); - mTrafficChannelEndListener = null; - mTrafficChannelCallEvent = null; + broadcast(new ChannelEvent(mChannel, ChannelEvent.Event.REQUEST_DISABLE)); } } /** * Broadcasts the call event to the registered listener */ - protected void broadcast(CallEvent event) + protected void broadcast(IDecodeEvent event) { - if(mCallEventListener != null) + if(mDecodeEventListener != null) { - mCallEventListener.receive(event); + mDecodeEventListener.receive(event); + } + } + + /** + * Broadcasts the channel event to a registered listener + */ + private void broadcast(ChannelEvent channelEvent) + { + if(mChannelEventListener != null) + { + mChannelEventListener.receive(channelEvent); } } @Override - public void addCallEventListener(Listener listener) + public void setChannelEventListener(Listener listener) { - mCallEventListener = listener; + mChannelEventListener = listener; } @Override - public void removeCallEventListener(Listener listener) + public void removeChannelEventListener() { - mCallEventListener = null; + mChannelEventListener = null; } - - //NEW: @Override - public Listener getAttributeChangeRequestListener() + public void addDecodeEventListener(Listener listener) { - return mMutableMetadata; + mDecodeEventListener = listener; + } + + @Override + public void removeDecodeEventListener(Listener listener) + { + mDecodeEventListener = null; } /** @@ -483,57 +559,6 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder return mDecoderStateEventReceiver; } - /** - * Registers a listener to be notified when a traffic channel call event is - * completed, so that the listener can perform call tear-down - */ - public void configureAsTrafficChannel(TrafficChannelManager manager, CallEvent callEvent) - { - mTrafficChannelEndListener = manager; - - mTrafficChannelCallEvent = callEvent; - - /* Broadcast the call event details as metadata for the audio manager */ - String channel = mTrafficChannelCallEvent.getChannel(); - - if(channel != null) - { - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, channel)); - } - - long frequency = mTrafficChannelCallEvent.getFrequency(); - - if(frequency > 0) - { - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, frequency)); - } - - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.PRIMARY_DECODER_TYPE, - mTrafficChannelCallEvent.getDecoderType())); - - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_CONFIGURATION_NAME, "TRAFFIC")); - - String from = mTrafficChannelCallEvent.getFromID(); - - if(from != null) - { - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_FROM, from, - mTrafficChannelCallEvent.getFromIDAlias())); - } - - String to = mTrafficChannelCallEvent.getToID(); - - if(to != null) - { - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, to, - mTrafficChannelCallEvent.getToIDAlias())); - } - - /* Rebroadcast the allocation event so that the internal decoder states - * can self-configure with the call event details */ - broadcast(mTrafficChannelCallEvent); - } - /** * Registers the listener to receive source events from the channel state */ @@ -572,7 +597,8 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder //in an active (ie sync locked) state. if(getState().isActiveState()) { - broadcast(SourceEvent.frequencyErrorMeasurementSyncLocked(sourceEvent.getValue().longValue(), mChannelType.name())); + broadcast(SourceEvent.frequencyErrorMeasurementSyncLocked(sourceEvent.getValue().longValue(), + mChannel.getChannelType().name())); } break; } @@ -598,7 +624,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder case CHANGE_CALL_TIMEOUT: if(event instanceof ChangeChannelTimeoutEvent) { - ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent) event; + ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event; if(timeout.getChannelType() == ChannelType.STANDARD) { @@ -618,7 +644,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder } break; case END: - if(isTrafficChannel()) + if(mChannel.isTrafficChannel()) { setState(State.TEARDOWN); } @@ -628,7 +654,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder } break; case RESET: - /* Channel State does not respond to reset events */ + /* Channel State does not respond to reset events */ break; default: break; @@ -660,7 +686,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder } else if(mState == State.FADE && mEndTimeout <= System.currentTimeMillis()) { - if(isTrafficChannel()) + if(mChannel.isTrafficChannel()) { processTeardownState(); } diff --git a/src/main/java/io/github/dsheirer/channel/state/DecoderState.java b/src/main/java/io/github/dsheirer/channel/state/DecoderState.java index 0ce9cfc2..8eead5af 100644 --- a/src/main/java/io/github/dsheirer/channel/state/DecoderState.java +++ b/src/main/java/io/github/dsheirer/channel/state/DecoderState.java @@ -1,15 +1,23 @@ package io.github.dsheirer.channel.state; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; -import io.github.dsheirer.channel.metadata.IAttributeChangeRequestProvider; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierUpdateListener; +import io.github.dsheirer.identifier.IdentifierUpdateNotification; +import io.github.dsheirer.identifier.IdentifierUpdateProvider; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.identifier.configuration.ChannelDescriptorConfigurationIdentifier; +import io.github.dsheirer.identifier.configuration.DecoderTypeConfigurationIdentifier; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessageListener; -import io.github.dsheirer.message.Message; import io.github.dsheirer.module.Module; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.event.ActivitySummaryProvider; import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.event.ICallEventProvider; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEventProvider; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; @@ -19,47 +27,87 @@ import io.github.dsheirer.sample.Listener; * * Provides access to a textual activity summary of events observed. */ -public abstract class DecoderState extends Module implements ActivitySummaryProvider, Listener, - IAttributeChangeRequestProvider, ICallEventProvider, IDecoderStateEventListener, - IDecoderStateEventProvider, IMessageListener +public abstract class DecoderState extends Module implements ActivitySummaryProvider, Listener, + IDecodeEventProvider, IDecoderStateEventListener, IDecoderStateEventProvider, IMessageListener, + IdentifierUpdateProvider, IdentifierUpdateListener { +// private final static Logger mLog = LoggerFactory.getLogger(DecoderState.class); + protected String DIVIDER1 = "======================================================\n"; protected String DIVIDER2 = "------------------------------------------------------\n"; /* This has to be a broadcaster in order for references to persist */ - private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); - private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); + private Broadcaster mDecodeEventBroadcaster = new Broadcaster<>(); private Listener mDecoderStateListener; - private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener(); + private MutableIdentifierCollection mIdentifierCollection = new MutableIdentifierCollection(); + private ConfigurationIdentifierListener mConfigurationIdentifierListener = new ConfigurationIdentifierListener(); + private IChannelDescriptor mCurrentChannel; protected CallEvent mCurrentCallEvent; - private AliasList mAliasList; - - public DecoderState(AliasList aliasList) + public DecoderState() { - mAliasList = aliasList; + mIdentifierCollection.update(new DecoderTypeConfigurationIdentifier(getDecoderType())); + } + + @Override + public void start() + { + //Broadcast the existing identifiers (as add events) so that they can be received by external listeners + mIdentifierCollection.broadcastIdentifiers(); } public abstract DecoderType getDecoderType(); - public AliasList getAliasList() + /** + * Current collection of identifiers managed by the decoder state. + */ + public MutableIdentifierCollection getIdentifierCollection() { - return mAliasList; + return mIdentifierCollection; } - public boolean hasAliasList() + /** + * Registers the listener to receive identifier update notifications + */ + @Override + public void setIdentifierUpdateListener(Listener listener) { - return mAliasList != null; + getIdentifierCollection().setIdentifierUpdateListener(listener); + } + + /** + * Removes the listener from receiving identifier update notifications + */ + @Override + public void removeIdentifierUpdateListener() + { + getIdentifierCollection().removeIdentifierUpdateListener(); } /** * Provides subclass reference to the call event broadcaster */ - protected Broadcaster getCallEventBroadcaster() + protected Broadcaster getDecodeEventBroadcaster() { - return mCallEventBroadcaster; + return mDecodeEventBroadcaster; + } + + + @Override + public Listener getMessageListener() + { + return this; + } + + /** + * Resets the current temporal state variables to end the current event and prepare for the next. + */ + protected void resetState() + { + mIdentifierCollection.remove(IdentifierClass.USER); + mCurrentChannel = null; } /** @@ -85,11 +133,9 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv */ public void dispose() { - mCallEventBroadcaster.dispose(); - mCallEventBroadcaster = null; + mDecodeEventBroadcaster.dispose(); + mDecodeEventBroadcaster = null; mDecoderStateListener = null; - - mAliasList = null; } /** @@ -98,29 +144,29 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv public abstract String getActivitySummary(); /** - * Broadcasts a call event to any registered listeners + * Broadcasts a decode event to any registered listeners */ - protected void broadcast(CallEvent event) + protected void broadcast(IDecodeEvent event) { - mCallEventBroadcaster.broadcast(event); + mDecodeEventBroadcaster.broadcast(event); } /** * Adds a call event listener */ @Override - public void addCallEventListener(Listener listener) + public void addDecodeEventListener(Listener listener) { - mCallEventBroadcaster.addListener(listener); + mDecodeEventBroadcaster.addListener(listener); } /** * Removes the call event listener */ @Override - public void removeCallEventListener(Listener listener) + public void removeDecodeEventListener(Listener listener) { - mCallEventBroadcaster.removeListener(listener); + mDecodeEventBroadcaster.removeListener(listener); } @Override @@ -167,43 +213,66 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv mDecoderStateListener = null; } - @Override - public Listener getMessageListener() - { - return this; - } - /** - * Broadcasts the attribute change request to the registered listener - */ - protected void broadcast(AttributeChangeRequest request) - { - mAttributeChangeRequestBroadcaster.broadcast(request); - } - - /** - * Sets the listener to receive attribute change requests from this decoder state + * Listens for configuration identifiers and adds them to this decoder state's identifier collection. + * @return */ @Override - public void setAttributeChangeRequestListener(Listener listener) + public Listener getIdentifierUpdateListener() { - mAttributeChangeRequestBroadcaster.addListener(listener); + return mConfigurationIdentifierListener; } /** - * Removes any listener from receiving attribute change requests + * Configuration identifier listener. */ - @Override - public void removeAttributeChangeRequestListener(Listener listener) + public ConfigurationIdentifierListener getConfigurationIdentifierListener() { - mAttributeChangeRequestBroadcaster.removeListener(listener); + return mConfigurationIdentifierListener; } /** - * Broadaster so that Attribute monitors can broadcast attribute change requests + * Optional current channel descriptor */ - protected Listener getAttributeChangeRequestListener() + protected IChannelDescriptor getCurrentChannel() { - return mAttributeChangeRequestBroadcaster; + return mCurrentChannel; + } + + /** + * Sets the current channel descriptor + */ + protected void setCurrentChannel(IChannelDescriptor channel) + { + mCurrentChannel = channel; + } + + /** + * Listener for configuration type identifier updates sent from the channel state. Adds configuration + * identifiers to this decoder state so that decode events will contain configuration details in the + * event's identifier collection. + */ + public class ConfigurationIdentifierListener implements Listener + { + @Override + public void receive(IdentifierUpdateNotification identifierUpdateNotification) + { + if(identifierUpdateNotification.getIdentifier().getIdentifierClass() == IdentifierClass.CONFIGURATION && + identifierUpdateNotification.getIdentifier().getForm() != Form.DECODER_TYPE && + identifierUpdateNotification.getIdentifier().getForm() != Form.CHANNEL_DESCRIPTOR) + { + getIdentifierCollection().update(identifierUpdateNotification.getIdentifier()); + } + + if(identifierUpdateNotification.getOperation() == IdentifierUpdateNotification.Operation.ADD) + { + Identifier identifier = identifierUpdateNotification.getIdentifier(); + + if(identifier instanceof ChannelDescriptorConfigurationIdentifier) + { + mCurrentChannel = ((ChannelDescriptorConfigurationIdentifier)identifier).getValue(); + } + } + } } } diff --git a/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java b/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java index ad853865..90d868a1 100644 --- a/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java +++ b/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java @@ -62,7 +62,6 @@ public class DecoderStateEvent END, RESET, SOURCE_FREQUENCY, - START, - TRAFFIC_CHANNEL_ALLOCATION; + START; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/channel/state/IDecoderStateEventProvider.java b/src/main/java/io/github/dsheirer/channel/state/IDecoderStateEventProvider.java index 0e387e2e..e0b27fe7 100644 --- a/src/main/java/io/github/dsheirer/channel/state/IDecoderStateEventProvider.java +++ b/src/main/java/io/github/dsheirer/channel/state/IDecoderStateEventProvider.java @@ -4,6 +4,6 @@ import io.github.dsheirer.sample.Listener; public interface IDecoderStateEventProvider { - public void setDecoderStateListener( Listener listener ); - public void removeDecoderStateListener(); + void setDecoderStateListener( Listener listener ); + void removeDecoderStateListener(); } diff --git a/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelAllocationEvent.java b/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelAllocationEvent.java deleted file mode 100644 index 935e60e9..00000000 --- a/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelAllocationEvent.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.channel.traffic; - -import io.github.dsheirer.channel.state.DecoderStateEvent; -import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.module.decode.event.CallEvent; - -/** - * Traffic channel allocation event wraps a call event indicating the channel - * and frequency for a traffic channel allocation event, so that the traffic - * channel manager can respond and allocate a decoder channel and source if - * available, or change the wrapped call event to a call detect event and log it. - */ -public class TrafficChannelAllocationEvent extends DecoderStateEvent -{ - private CallEvent mCallEvent; - - public TrafficChannelAllocationEvent( Object source, CallEvent callEvent ) - { - super( source, Event.TRAFFIC_CHANNEL_ALLOCATION, State.CALL ); - mCallEvent = callEvent; - } - - public CallEvent getCallEvent() - { - return mCallEvent; - } -} diff --git a/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java b/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java deleted file mode 100644 index b81b2869..00000000 --- a/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java +++ /dev/null @@ -1,391 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2017 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.channel.traffic; - -import io.github.dsheirer.channel.state.DecoderStateEvent; -import io.github.dsheirer.channel.state.IDecoderStateEventListener; -import io.github.dsheirer.controller.channel.Channel; -import io.github.dsheirer.controller.channel.Channel.ChannelType; -import io.github.dsheirer.controller.channel.ChannelEvent; -import io.github.dsheirer.controller.channel.ChannelEvent.Event; -import io.github.dsheirer.controller.channel.ChannelModel; -import io.github.dsheirer.controller.channel.TrafficChannelEvent; -import io.github.dsheirer.module.Module; -import io.github.dsheirer.module.decode.config.DecodeConfiguration; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.event.CallEvent.CallEventType; -import io.github.dsheirer.module.decode.event.ICallEventProvider; -import io.github.dsheirer.record.config.RecordConfiguration; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.config.SourceConfigTuner; -import io.github.dsheirer.source.tuner.channel.TunerChannel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener -{ - private final static Logger mLog = LoggerFactory.getLogger(TrafficChannelManager.class); - public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED"; - public static final String NO_TUNER_AVAILABLE = "NO TUNER AVAILABLE"; - public static final String UNKNOWN_FREQUENCY = "UNKNOWN FREQUENCY"; - - private int mTrafficChannelPoolMaximumSize = DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT; - private List mTrafficChannelPool = new ArrayList(); - private Map mTrafficChannelsInUse = new ConcurrentHashMap(); - - private DecoderStateEventListener mEventListener = new DecoderStateEventListener(); - private Listener mCallEventListener; - - private ChannelModel mChannelModel; - private DecodeConfiguration mDecodeConfiguration; - private RecordConfiguration mRecordConfiguration; - private String mSystem; - private String mSite; - private String mAliasListName; - - /** - * Monitors call events and allocates traffic decoder channels in response - * to traffic channel allocation call events. Manages a pool of reusable - * traffic channel allocations. - * - * @param channelModel containing channels currently in use - * @param decodeConfiguration - decoder configuration to use for each - * traffic channel allocation. - * @param recordConfiguration - recording options for each traffic channel - * @param system label to use to describe the system - * @param site label to use to describe the site - * @param aliasListName designated for the channel - * @param trafficChannelPoolSize - maximum number of allocated traffic channels - * in the pool - */ - public TrafficChannelManager(ChannelModel channelModel, - DecodeConfiguration decodeConfiguration, - RecordConfiguration recordConfiguration, - String system, - String site, - String aliasListName, - int trafficChannelPoolSize) - { - mChannelModel = channelModel; - mDecodeConfiguration = decodeConfiguration; - mRecordConfiguration = recordConfiguration; - mSystem = system; - mSite = site; - mAliasListName = aliasListName; - mTrafficChannelPoolMaximumSize = trafficChannelPoolSize; - } - - @Override - public void dispose() - { - for(Channel trafficChannel : mTrafficChannelPool) - { - mChannelModel.broadcast(new ChannelEvent(trafficChannel, Event.REQUEST_DISABLE)); - } - - mTrafficChannelPool.clear(); - - mTrafficChannelsInUse.clear(); - - mCallEventListener = null; - mDecodeConfiguration = null; - } - - /** - * Provides a channel by either reusing an existing channel or constructing - * a new one, limited by the total number of constructed channels allowed. - * - * Note: you must enforce thread safety on the mTrafficChannelsInUse - * external to this method. - * - * @param channelNumber - * @param tunerChannel - * @return - */ - private Channel getChannel(String channelNumber, TunerChannel tunerChannel) - { - Channel channel = null; - - if(!mTrafficChannelsInUse.containsKey(channelNumber)) - { - for(Channel configuredChannel : mTrafficChannelPool) - { - if(!configuredChannel.isProcessing()) - { - channel = configuredChannel; - break; - } - } - - if(channel == null && mTrafficChannelPool.size() < mTrafficChannelPoolMaximumSize) - { - channel = new Channel("Traffic", ChannelType.TRAFFIC); - - channel.setDecodeConfiguration(mDecodeConfiguration); - - channel.setRecordConfiguration(mRecordConfiguration); - - channel.setAliasListName(mAliasListName); - - mChannelModel.addChannel(channel); - - mTrafficChannelPool.add(channel); - } - - /* If we have a configured channel, update metadata */ - if(channel != null) - { - channel.setSourceConfiguration(new SourceConfigTuner(tunerChannel)); - channel.setSystem(mSystem); - channel.setSite(mSite); - channel.setName(channelNumber); - - mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_CONFIGURATION_CHANGE)); - } - } - - return channel; - } - - /** - * Processes the event and creates a traffic channel is resources are - * available - */ - private void process(TrafficChannelAllocationEvent event) - { - CallEvent callEvent = event.getCallEvent(); - - /* Check for duplicate events and suppress */ - synchronized(mTrafficChannelsInUse) - { - if(mTrafficChannelsInUse.containsKey(callEvent.getChannel())) - { - return; - } - - long frequency = callEvent.getFrequency(); - - if(frequency > 0) - { - Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(frequency, - mDecodeConfiguration.getChannelSpecification().getBandwidth())); - - if(channel != null) - { - TrafficChannelEvent trafficChannelEvent = - new TrafficChannelEvent(this, channel, Event.REQUEST_ENABLE, callEvent); - - //Request to enable the channel - mChannelModel.broadcast(trafficChannelEvent); - - if(channel.isProcessing()) - { - mTrafficChannelsInUse.put(callEvent.getChannel(), channel); - } - else - { - callEvent.setCallEventType(CallEventType.CALL_DETECT); - - String details = callEvent.getDetails(); - - if(details == null || details.isEmpty()) - { - callEvent.setDetails(CHANNEL_START_REJECTED); - } - else if(!details.contains(CHANNEL_START_REJECTED)) - { - callEvent.setDetails(new StringBuilder(CHANNEL_START_REJECTED).append(" : ") - .append(callEvent.getDetails()).toString()); - } - } - } - else - { - callEvent.setCallEventType(CallEventType.CALL_DETECT); - - String details = callEvent.getDetails(); - - if(details == null || details.isEmpty()) - { - callEvent.setDetails(NO_TUNER_AVAILABLE); - } - else if(!details.contains(NO_TUNER_AVAILABLE)) - { - callEvent.setDetails(new StringBuilder(NO_TUNER_AVAILABLE).append(" : ") - .append(callEvent.getDetails()).toString()); - } - } - } - else - { - callEvent.setCallEventType(CallEventType.CALL_DETECT); - - String details = callEvent.getDetails(); - - if(details == null || details.isEmpty()) - { - callEvent.setDetails(UNKNOWN_FREQUENCY); - } - else if(!details.contains(UNKNOWN_FREQUENCY)) - { - callEvent.setDetails(new StringBuilder(UNKNOWN_FREQUENCY).append(" : ") - .append(callEvent.getDetails()).toString()); - } - } - - final Listener listener = mCallEventListener; - - if(listener != null) - { - listener.receive(callEvent); - } - } - } - - /** - * Compares the call type, channel and to fields for equivalence and the - * from field for either both null, or equivalence. - * - * @param e1 - * @param e2 - * @return - */ - public static boolean isSameCallEvent(CallEvent e1, CallEvent e2) - { - if(e1 == null || e2 == null) - { - return false; - } - - if(e1.getCallEventType() != e2.getCallEventType()) - { - return false; - } - - if(e1.getChannel() == null || e2.getChannel() == null || !e1.getChannel().contentEquals(e2.getChannel())) - { - return false; - } - - if(e1.getToID() == null || e2.getToID() == null || !e1.getToID().contentEquals(e2.getToID())) - { - return false; - } - - if(e1.getFromID() == null || e2.getFromID() == null) - { - return (e1.getFromID() == null && e2.getFromID() == null); - } - else if(e1.getFromID().contentEquals(e2.getFromID())) - { - return true; - } - - return false; - } - - @Override - public Listener getDecoderStateListener() - { - return mEventListener; - } - - @Override - public void addCallEventListener(Listener listener) - { - mCallEventListener = listener; - } - - @Override - public void removeCallEventListener(Listener listener) - { - mCallEventListener = null; - } - - @Override - public void reset() - { - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - if(!mTrafficChannelsInUse.isEmpty()) - { - List channels = new ArrayList<>(); - - /* Copy the keyset so we don't get concurrent modification of the map */ - channels.addAll(mTrafficChannelsInUse.keySet()); - - for(String channel : channels) - { - callEnd(channel); - } - } - } - - /** - * Callback used by the TrafficChannelStatusListener class to signal the - * end of a an allocated traffic channel call event - * - * @param channelNumber - channel number from the call event that signaled - * the start of a traffic channel allocation - */ - public void callEnd(String channelNumber) - { - synchronized(mTrafficChannelsInUse) - { - if(channelNumber != null && mTrafficChannelsInUse.containsKey(channelNumber)) - { - Channel channel = mTrafficChannelsInUse.get(channelNumber); - - mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_DISABLE)); - - mTrafficChannelsInUse.remove(channelNumber); - } - } - } - - /** - * Wrapper class for the decoder state event listener interface to catch - * traffic channel allocation requests - */ - public class DecoderStateEventListener implements Listener - { - @Override - public void receive(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case TRAFFIC_CHANNEL_ALLOCATION: - process((TrafficChannelAllocationEvent) event); - break; - default: - break; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/controller/ControllerPanel.java b/src/main/java/io/github/dsheirer/controller/ControllerPanel.java index 95bcd3d3..64f0eb32 100644 --- a/src/main/java/io/github/dsheirer/controller/ControllerPanel.java +++ b/src/main/java/io/github/dsheirer/controller/ControllerPanel.java @@ -21,10 +21,10 @@ package io.github.dsheirer.controller; import com.jidesoft.swing.JideTabbedPane; import io.github.dsheirer.alias.AliasController; import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.audio.AudioManager; -import io.github.dsheirer.audio.AudioPanel; import io.github.dsheirer.audio.broadcast.BroadcastModel; import io.github.dsheirer.audio.broadcast.BroadcastPanel; +import io.github.dsheirer.audio.playback.AudioPanel; +import io.github.dsheirer.audio.playback.AudioPlaybackManager; import io.github.dsheirer.channel.metadata.ChannelMetadataViewer; import io.github.dsheirer.controller.channel.ChannelController; import io.github.dsheirer.controller.channel.ChannelModel; @@ -33,14 +33,17 @@ import io.github.dsheirer.controller.channel.map.ChannelMapModel; import io.github.dsheirer.icon.IconManager; import io.github.dsheirer.map.MapPanel; import io.github.dsheirer.map.MapService; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.SourceManager; import io.github.dsheirer.source.tuner.TunerModel; import io.github.dsheirer.source.tuner.TunerViewPanel; import net.miginfocom.swing.MigLayout; -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; +import javax.swing.JTable; +import java.awt.Color; +import java.awt.Dimension; public class ControllerPanel extends JPanel { @@ -59,7 +62,7 @@ public class ControllerPanel extends JPanel private JideTabbedPane mTabbedPane; protected JTable mChannelActivityTable = new JTable(); - public ControllerPanel(AudioManager audioManager, + public ControllerPanel(AudioPlaybackManager audioPlaybackManager, AliasModel aliasModel, BroadcastModel broadcastModel, ChannelModel channelModel, @@ -69,20 +72,24 @@ public class ControllerPanel extends JPanel MapService mapService, SettingsManager settingsManager, SourceManager sourceManager, - TunerModel tunerModel) + TunerModel tunerModel, + UserPreferences userPreferences) { mBroadcastModel = broadcastModel; mChannelModel = channelModel; - mAudioPanel = new AudioPanel(iconManager, settingsManager, sourceManager, audioManager); + mAudioPanel = new AudioPanel(iconManager, userPreferences, settingsManager, sourceManager, audioPlaybackManager, + aliasModel); - mChannelMetadataViewer = new ChannelMetadataViewer(channelProcessingManager, iconManager, settingsManager); + mChannelMetadataViewer = new ChannelMetadataViewer(channelModel, channelProcessingManager, iconManager, + aliasModel, userPreferences); - mMapPanel = new MapPanel(mapService, iconManager, settingsManager); + mMapPanel = new MapPanel(mapService, aliasModel, iconManager, settingsManager); mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager); - mChannelController = new ChannelController(channelModel, channelMapModel, sourceManager, aliasModel); + mChannelController = new ChannelController(channelModel, channelProcessingManager, channelMapModel, + sourceManager, aliasModel); mAliasController = new AliasController(aliasModel, broadcastModel, iconManager); diff --git a/src/main/java/io/github/dsheirer/controller/channel/Channel.java b/src/main/java/io/github/dsheirer/controller/channel/Channel.java index df432101..59f41908 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/Channel.java +++ b/src/main/java/io/github/dsheirer/controller/channel/Channel.java @@ -1,4 +1,5 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk * Copyright (C) 2014-2018 Dennis Sheirer * @@ -14,8 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -38,10 +39,16 @@ import io.github.dsheirer.source.config.SourceConfigRecording; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfiguration; import io.github.dsheirer.source.tuner.channel.TunerChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; @JacksonXmlRootElement(localName = "channel") public class Channel extends Configuration implements Listener { + private final static Logger mLog = LoggerFactory.getLogger(Channel.class); + // Standard channels are persisted and traffic channels are temporary public enum ChannelType { @@ -189,6 +196,18 @@ public class Channel extends Configuration implements Listener return mChannelType; } + @JsonIgnore + public boolean isTrafficChannel() + { + return mChannelType == ChannelType.TRAFFIC; + } + + @JsonIgnore + public boolean isStandardChannel() + { + return mChannelType == ChannelType.STANDARD; + } + /** * Returns the owning system for this channel. */ @@ -365,6 +384,14 @@ public class Channel extends Configuration implements Listener mAliasListName = name; } + /** + * Indicates if this channel has a non-null, non-empty alias list name specified. + */ + public boolean hasAliasList() + { + return mAliasListName != null && !mAliasListName.isEmpty(); + } + /** * Gets the primary decoder configuration used by this channel */ @@ -547,4 +574,25 @@ public class Channel extends Configuration implements Listener { mChannelFrequencyCorrection = 0; } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + Channel channel = (Channel)o; + return getChannelID() == channel.getChannelID(); + } + + @Override + public int hashCode() + { + return Objects.hash(getChannelID()); + } } diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelAutoStartFrame.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelAutoStartFrame.java index 92dbc945..5971a1ca 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelAutoStartFrame.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelAutoStartFrame.java @@ -1,20 +1,25 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; +import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.ThreadPool; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -42,7 +47,7 @@ public class ChannelAutoStartFrame extends JFrame private final static Logger mLog = LoggerFactory.getLogger(ChannelAutoStartFrame.class); private static final int COUNTDOWN_SECONDS = 10; - private ChannelEventListener mChannelEventListener; + private Listener mChannelEventListener; private List mChannels; private JLabel mCountdownLabel; @@ -61,27 +66,22 @@ public class ChannelAutoStartFrame extends JFrame * @param listener to receive channel start/enable request(s) * @param channels to auto-start */ - public ChannelAutoStartFrame(ChannelEventListener listener, List channels) + public ChannelAutoStartFrame(Listener listener, List channels) { mChannelEventListener = listener; mChannels = channels; init(); - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - setVisible(true); - startTimer(); - } + EventQueue.invokeLater(() -> { + setVisible(true); + startTimer(); }); } private void init() { setTitle("Auto-Start Channels"); - setSize(new Dimension(400,300)); + setSize(new Dimension(400, 300)); setLocationRelativeTo(null); addWindowListener(new WindowAdapter() { @@ -95,7 +95,7 @@ public class ChannelAutoStartFrame extends JFrame }); JPanel panel = new JPanel(); - panel.setLayout(new MigLayout("","[grow,fill][grow,fill]", + panel.setLayout(new MigLayout("", "[grow,fill][grow,fill]", "[][][grow,fill][]")); panel.add(new JLabel("The following channels will be automatically"), "span"); @@ -138,6 +138,7 @@ public class ChannelAutoStartFrame extends JFrame /** * Updates the countdown text label on the swing event thread + * * @param value for the countdown */ private void updateCountdownText(final int value) @@ -180,9 +181,9 @@ public class ChannelAutoStartFrame extends JFrame { if(mChannelEventListener != null) { - for(Channel channel: mChannels) + for(Channel channel : mChannels) { - mChannelEventListener.channelChanged(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE)); + mChannelEventListener.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE)); } } } @@ -210,14 +211,7 @@ public class ChannelAutoStartFrame extends JFrame mTimerFuture = null; } - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - dispose(); - } - }); + EventQueue.invokeLater(() -> dispose()); } /** diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java index 06d1becb..9963ea7f 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java @@ -41,254 +41,257 @@ import java.awt.event.ActionListener; public class ChannelController extends JPanel implements ActionListener, ListSelectionListener { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private ChannelModel mChannelModel; - private JTable mChannelTable; - private TableFilterHeader mTableFilterHeader; - private ChannelEditor mEditor; - - private static final String NEW_CHANNEL = "New"; - private static final String COPY_CHANNEL = "Copy"; - private static final String DELETE_CHANNEL = "Delete"; - - private JButton mNewChannelButton = new JButton( NEW_CHANNEL ); - private JButton mCopyChannelButton = new JButton( COPY_CHANNEL ); - private JButton mDeleteChannelButton = new JButton( DELETE_CHANNEL ); + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private JTable mChannelTable; + private TableFilterHeader mTableFilterHeader; + private ChannelEditor mEditor; - public ChannelController( ChannelModel channelModel, - ChannelMapModel channelMapModel, - SourceManager sourceManager, - AliasModel aliasModel ) - { - mChannelModel = channelModel; + private static final String NEW_CHANNEL = "New"; + private static final String COPY_CHANNEL = "Copy"; + private static final String DELETE_CHANNEL = "Delete"; - mEditor = new ChannelEditor( channelModel, channelMapModel, sourceManager, aliasModel ); - mChannelModel.addListener( mEditor ); + private JButton mNewChannelButton = new JButton(NEW_CHANNEL); + private JButton mCopyChannelButton = new JButton(COPY_CHANNEL); + private JButton mDeleteChannelButton = new JButton(DELETE_CHANNEL); - init(); - } - - private void init() - { - setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]") ); + public ChannelController(ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + ChannelMapModel channelMapModel, + SourceManager sourceManager, + AliasModel aliasModel) + { + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mEditor = new ChannelEditor(channelModel, channelProcessingManager, channelMapModel, sourceManager, aliasModel); + mChannelModel.addListener(mEditor); + mChannelProcessingManager.addChannelEventListener(mEditor); - //System Configuration View and Editor - mChannelTable = new JTable( mChannelModel ); - mChannelTable.setDefaultRenderer( String.class, new ChannelTableCellRenderer() ); - mChannelTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - mChannelTable.getSelectionModel().addListSelectionListener( this ); - mChannelTable.setAutoCreateRowSorter( true ); - - mTableFilterHeader = new TableFilterHeader( mChannelTable, AutoChoices.ENABLED ); - mTableFilterHeader.setFilterOnUpdates( true ); - - JScrollPane channelScroller = new JScrollPane( mChannelTable ); + init(); + } - JPanel buttonsPanel = new JPanel(); - - buttonsPanel.setLayout( - new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); + private void init() + { + setLayout(new MigLayout("", "[grow,fill]", "[grow,fill]")); - mNewChannelButton.addActionListener( this ); - mNewChannelButton.setToolTipText( "Create a new channel configuration" ); - buttonsPanel.add( mNewChannelButton ); - - mCopyChannelButton.addActionListener( this ); - mCopyChannelButton.setEnabled( false ); - mCopyChannelButton.setToolTipText( "Create a copy of the currently selected channel configuration" ); - buttonsPanel.add( mCopyChannelButton ); + //System Configuration View and Editor + mChannelTable = new JTable(mChannelModel); + mChannelTable.setDefaultRenderer(String.class, new ChannelTableCellRenderer()); + mChannelTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + mChannelTable.getSelectionModel().addListSelectionListener(this); + mChannelTable.setAutoCreateRowSorter(true); - mDeleteChannelButton.addActionListener( this ); - mDeleteChannelButton.setEnabled( false ); - mDeleteChannelButton.setToolTipText( "Delete the currently selected channel configuration" ); - buttonsPanel.add( mDeleteChannelButton ); - - JPanel listAndButtonsPanel = new JPanel(); + mTableFilterHeader = new TableFilterHeader(mChannelTable, AutoChoices.ENABLED); + mTableFilterHeader.setFilterOnUpdates(true); - listAndButtonsPanel.setLayout( - new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); + JScrollPane channelScroller = new JScrollPane(mChannelTable); - listAndButtonsPanel.add( channelScroller, "wrap" ); - listAndButtonsPanel.add( buttonsPanel ); - - JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); - splitPane.setDividerSize( 5 ); - splitPane.setShowGripper( true ); + JPanel buttonsPanel = new JPanel(); - //Shrink this guy so that he'll eventually be expanded to the scroller preferred size - listAndButtonsPanel.setPreferredSize( new Dimension( 10, 10 ) ); + buttonsPanel.setLayout( + new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]")); - //Attempt to get a 60:40 vertical sizing preference - JScrollPane listScroller = new JScrollPane( listAndButtonsPanel ); - listScroller.setPreferredSize( new Dimension( 800, 58 ) ); - - JScrollPane editorScroller = new JScrollPane( mEditor ); - editorScroller.setPreferredSize( new Dimension( 800, 42 ) ); + mNewChannelButton.addActionListener(this); + mNewChannelButton.setToolTipText("Create a new channel configuration"); + buttonsPanel.add(mNewChannelButton); - splitPane.add( listScroller ); - splitPane.add( editorScroller ); + mCopyChannelButton.addActionListener(this); + mCopyChannelButton.setEnabled(false); + mCopyChannelButton.setToolTipText("Create a copy of the currently selected channel configuration"); + buttonsPanel.add(mCopyChannelButton); - add( splitPane ); - } + mDeleteChannelButton.addActionListener(this); + mDeleteChannelButton.setEnabled(false); + mDeleteChannelButton.setToolTipText("Delete the currently selected channel configuration"); + buttonsPanel.add(mDeleteChannelButton); - @Override - public void valueChanged( ListSelectionEvent event ) - { - //This limits event firing to only when selection is complete - if( !event.getValueIsAdjusting() ) - { - int selectedRow = mChannelTable.getSelectedRow(); - - if( selectedRow != -1 ) - { - int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); - - Channel channel = mChannelModel.getChannelAtIndex( index ); - - if( channel != null ) - { - mEditor.setItem( channel ); - mCopyChannelButton.setEnabled( true ); - mDeleteChannelButton.setEnabled( true ); - } - } - else - { - mEditor.setItem( null ); - mCopyChannelButton.setEnabled( false ); - mDeleteChannelButton.setEnabled( false ); - } - } - } + JPanel listAndButtonsPanel = new JPanel(); - /** - * Adds the channel to the channel model/table and scrolls to view it - */ - private void addChannel( Channel channel ) - { - //HACK: when inserting a row to the model, the JTable gets - //notified and attempts to tell the coderazzi table filter - //adaptive choices filter to refresh before the table filter is - //notified of the row additions, causing an index out of bounds - //exception. We turn off adaptive choices temporarily, add the - //channel, and turn on adaptive choices again. - mTableFilterHeader.setAdaptiveChoices( false ); - - int index = mChannelModel.addChannel( channel ); + listAndButtonsPanel.setLayout( + new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]")); - mTableFilterHeader.setAdaptiveChoices( true ); + listAndButtonsPanel.add(channelScroller, "wrap"); + listAndButtonsPanel.add(buttonsPanel); - if( index >= 0 ) - { - int translatedIndex = mChannelTable.convertRowIndexToView( index ); + JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT); + splitPane.setDividerSize(5); + splitPane.setShowGripper(true); - if(translatedIndex >= 0) - { - mChannelTable.setRowSelectionInterval( translatedIndex, translatedIndex ); - mChannelTable.scrollRectToVisible( - new Rectangle( mChannelTable.getCellRect( translatedIndex, 0, true ) ) ); - } - } - } - - private Channel getSelectedChannel() - { - int index = mChannelTable.getSelectedRow(); - - if( index >= 0 ) - { - return mChannelModel.getChannelAtIndex( - mChannelTable.convertRowIndexToModel( index ) ); - } - - return null; - } + //Shrink this guy so that he'll eventually be expanded to the scroller preferred size + listAndButtonsPanel.setPreferredSize(new Dimension(10, 10)); - /** - * Responds to New, Copy and Delete Channel button invocations - */ - @Override - public void actionPerformed( ActionEvent event ) - { - switch( event.getActionCommand() ) - { - case NEW_CHANNEL: - addChannel( new Channel( "New Channel" ) ); - break; - case COPY_CHANNEL: - Channel selected = getSelectedChannel(); - - if( selected != null ) - { - addChannel( selected.copyOf() ); - } - break; - case DELETE_CHANNEL: - Channel toDelete = getSelectedChannel(); + //Attempt to get a 60:40 vertical sizing preference + JScrollPane listScroller = new JScrollPane(listAndButtonsPanel); + listScroller.setPreferredSize(new Dimension(800, 58)); - if( toDelete != null ) - { - int choice = JOptionPane.showConfirmDialog( ChannelController.this, - "Do you want to delete this channel?", "Delete Channel?", - JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); - - if( choice == JOptionPane.YES_OPTION ) - { - mChannelModel.removeChannel( toDelete ); - } - } - break; - default: - break; - } - } - - public class ChannelTableCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; + JScrollPane editorScroller = new JScrollPane(mEditor); + editorScroller.setPreferredSize(new Dimension(800, 42)); - @Override - public Component getTableCellRendererComponent( JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column ) - { - Component component = super.getTableCellRendererComponent( - table, value, isSelected, hasFocus, row, column ); + splitPane.add(listScroller); + splitPane.add(editorScroller); - ChannelModel model = (ChannelModel)table.getModel(); + add(splitPane); + } - int index = table.convertRowIndexToModel( row ); - - Channel channel = model.getChannelAtIndex( index ); - - boolean enabled = channel.isProcessing(); - - if( isSelected ) - { - if( enabled ) - { - component.setBackground( Color.YELLOW ); - } - else - { - component.setBackground( table.getSelectionBackground() ); - } - } - else - { - if( enabled ) - { - component.setBackground( Color.GREEN ); - } - else - { - component.setBackground( table.getBackground() ); - } - } - - return component; - } - - } + @Override + public void valueChanged(ListSelectionEvent event) + { + //This limits event firing to only when selection is complete + if(!event.getValueIsAdjusting()) + { + int selectedRow = mChannelTable.getSelectedRow(); + + if(selectedRow != -1) + { + int index = mChannelTable.convertRowIndexToModel(mChannelTable.getSelectedRow()); + + Channel channel = mChannelModel.getChannelAtIndex(index); + + if(channel != null) + { + mEditor.setItem(channel); + mCopyChannelButton.setEnabled(true); + mDeleteChannelButton.setEnabled(true); + } + } + else + { + mEditor.setItem(null); + mCopyChannelButton.setEnabled(false); + mDeleteChannelButton.setEnabled(false); + } + } + } + + /** + * Adds the channel to the channel model/table and scrolls to view it + */ + private void addChannel(Channel channel) + { + //HACK: when inserting a row to the model, the JTable gets + //notified and attempts to tell the coderazzi table filter + //adaptive choices filter to refresh before the table filter is + //notified of the row additions, causing an index out of bounds + //exception. We turn off adaptive choices temporarily, add the + //channel, and turn on adaptive choices again. + mTableFilterHeader.setAdaptiveChoices(false); + + int index = mChannelModel.addChannel(channel); + + mTableFilterHeader.setAdaptiveChoices(true); + + if(index >= 0) + { + int translatedIndex = mChannelTable.convertRowIndexToView(index); + + if(translatedIndex >= 0) + { + mChannelTable.setRowSelectionInterval(translatedIndex, translatedIndex); + mChannelTable.scrollRectToVisible( + new Rectangle(mChannelTable.getCellRect(translatedIndex, 0, true))); + } + } + } + + private Channel getSelectedChannel() + { + int index = mChannelTable.getSelectedRow(); + + if(index >= 0) + { + return mChannelModel.getChannelAtIndex( + mChannelTable.convertRowIndexToModel(index)); + } + + return null; + } + + /** + * Responds to New, Copy and Delete Channel button invocations + */ + @Override + public void actionPerformed(ActionEvent event) + { + switch(event.getActionCommand()) + { + case NEW_CHANNEL: + addChannel(new Channel("New Channel")); + break; + case COPY_CHANNEL: + Channel selected = getSelectedChannel(); + + if(selected != null) + { + addChannel(selected.copyOf()); + } + break; + case DELETE_CHANNEL: + Channel toDelete = getSelectedChannel(); + + if(toDelete != null) + { + int choice = JOptionPane.showConfirmDialog(ChannelController.this, + "Do you want to delete this channel?", "Delete Channel?", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + + if(choice == JOptionPane.YES_OPTION) + { + mChannelModel.removeChannel(toDelete); + } + } + break; + default: + break; + } + } + + public class ChannelTableCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column) + { + Component component = super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); + + ChannelModel model = (ChannelModel)table.getModel(); + + int index = table.convertRowIndexToModel(row); + + Channel channel = model.getChannelAtIndex(index); + + boolean enabled = channel.isProcessing(); + + if(isSelected) + { + if(enabled) + { + component.setBackground(Color.YELLOW); + } + else + { + component.setBackground(table.getSelectionBackground()); + } + } + else + { + if(enabled) + { + component.setBackground(Color.GREEN); + } + else + { + component.setBackground(table.getBackground()); + } + } + + return component; + } + + } } diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelEditor.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelEditor.java index 65d2cade..7fa551c4 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelEditor.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelEditor.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; import com.jidesoft.swing.JideTabbedPane; @@ -26,6 +28,7 @@ import io.github.dsheirer.module.decode.AuxDecodeConfigurationEditor; import io.github.dsheirer.module.decode.DecodeConfigurationEditor; import io.github.dsheirer.module.log.EventLogConfigurationEditor; import io.github.dsheirer.record.RecordConfigurationEditor; +import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.SourceConfigurationEditor; import io.github.dsheirer.source.SourceManager; import net.miginfocom.swing.MigLayout; @@ -37,7 +40,7 @@ import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -public class ChannelEditor extends Editor implements ActionListener, ChannelEventListener +public class ChannelEditor extends Editor implements ActionListener, Listener { private static final long serialVersionUID = 1L; private static final String ACTION_START = "Start"; @@ -56,17 +59,20 @@ public class ChannelEditor extends Editor implements ActionListener, Ch private JLabel mChannelName = new JLabel("Channel:"); private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private ChannelMapModel mChannelMapModel; private SourceManager mSourceManager; private boolean mChannelStartRequested = false; public ChannelEditor(ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, ChannelMapModel channelMapModel, SourceManager sourceManager, AliasModel aliasModel) { mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mChannelMapModel = channelMapModel; mSourceManager = sourceManager; mNameConfigurationEditor = new NameConfigurationEditor(aliasModel, mChannelModel); @@ -126,7 +132,7 @@ public class ChannelEditor extends Editor implements ActionListener, Ch } @Override - public void channelChanged(ChannelEvent event) + public void receive(ChannelEvent event) { if(hasItem() && getItem() == event.getChannel()) { @@ -141,7 +147,7 @@ public class ChannelEditor extends Editor implements ActionListener, Ch case NOTIFICATION_DELETE: setItem(null); break; - case NOTIFICATION_START_PROCESSING_REJECTED: + case NOTIFICATION_PROCESSING_START_REJECTED: if(mChannelStartRequested) { JOptionPane.showMessageDialog(ChannelEditor.this, "Channel could not be " @@ -169,14 +175,14 @@ public class ChannelEditor extends Editor implements ActionListener, Ch save(); mChannelStartRequested = true; - mChannelModel.broadcast(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_ENABLE)); + mChannelProcessingManager.receive(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_ENABLE)); } } else if(command.contentEquals(ACTION_STOP)) { if(hasItem()) { - mChannelModel.broadcast(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_DISABLE)); + mChannelProcessingManager.receive(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_DISABLE)); } } else if(command.contentEquals(ACTION_SAVE)) @@ -246,9 +252,9 @@ public class ChannelEditor extends Editor implements ActionListener, Ch return; } + ChannelEvent update = new ChannelEvent(getItem(), ChannelEvent.Event.NOTIFICATION_CONFIGURATION_CHANGE); - mChannelModel.broadcast(new ChannelEvent(getItem(), - ChannelEvent.Event.NOTIFICATION_CONFIGURATION_CHANGE)); + mChannelModel.receive(update); } setModified(false); diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelEvent.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelEvent.java index 42679a38..1e565688 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelEvent.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelEvent.java @@ -1,83 +1,115 @@ -/******************************************************************************* - * SDR Trunk - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; public class ChannelEvent { - private Channel mChannel; - private Event mEvent; + private Channel mChannel; + private Event mEvent; + private String mDescription; - /** - * ChannelEvent - event describing any changes to channels - * @param channel - channel that changed - * @param event - change event - */ - public ChannelEvent( Channel channel, Event event ) - { - mChannel = channel; - mEvent = event; - } - - public Channel getChannel() - { - return mChannel; - } - - public Event getEvent() - { - return mEvent; - } + /** + * ChannelEvent - event describing channel related events + * + * @param channel for the event + * @param event that occurred + * @param description of the event + */ + public ChannelEvent(Channel channel, Event event, String description) + { + mChannel = channel; + mEvent = event; + mDescription = description; + } - public String toString() - { - return getEvent().name() + " Channel:" + getChannel().getName(); - } - - /** - * Channel events to describe the specific event - */ - public enum Event - { - //Channel is added - NOTIFICATION_ADD, - //Channel configuration has changed - NOTIFICATION_CONFIGURATION_CHANGE, - //Channel is deleted/removed - NOTIFICATION_DELETE, - //Channel enable request was rejected - NOTIFICATION_START_PROCESSING_REJECTED, - //Channel has started processing/decoding - NOTIFICATION_PROCESSING_START, - //Channel has stopped processing/decoding - NOTIFICATION_PROCESSING_STOP, - //Channel's selection state has changed - NOTIFICATION_SELECTION_CHANGE, - //Channel's channel-state has been reset - NOTIFICATION_STATE_RESET, - //Request to delete the channel - REQUEST_DELETE, - //Request to deselect the channel - REQUEST_DESELECT, - //Request to disable a channel - response will be a PROCESSING_STOP_NOTIFICATION - REQUEST_DISABLE, - //Request to enable a channel - response will be a PROCESSING_START_NOTIFICATION - REQUEST_ENABLE, - //Request to select the channel - REQUEST_SELECT; - } + /** + * ChannelEvent - event describing any changes to channels + * + * @param channel - channel that changed + * @param event - change event + */ + public ChannelEvent(Channel channel, Event event) + { + this(channel, event, null); + } + + /** + * Channel for the event + */ + public Channel getChannel() + { + return mChannel; + } + + /** + * Describes the event that happened to the channel + */ + public Event getEvent() + { + return mEvent; + } + + /** + * Optional description of the event + */ + public String getDescription() + { + return mDescription; + } + + public String toString() + { + return getEvent().name() + " Channel:" + getChannel().getName() + + (mDescription != null ? " Description:" + mDescription : ""); + } + + /** + * Channel events to describe the specific event + */ + public enum Event + { + //Channel is added + NOTIFICATION_ADD, + //Channel configuration has changed + NOTIFICATION_CONFIGURATION_CHANGE, + //Channel is deleted/removed + NOTIFICATION_DELETE, + //Channel enable request was rejected + NOTIFICATION_PROCESSING_START_REJECTED, + //Channel has started processing/decoding + NOTIFICATION_PROCESSING_START, + //Channel has stopped processing/decoding + NOTIFICATION_PROCESSING_STOP, + //Channel's selection state has changed + NOTIFICATION_SELECTION_CHANGE, + //Channel's channel-state has been reset + NOTIFICATION_STATE_RESET, + //Request to delete the channel + REQUEST_DELETE, + //Request to deselect the channel + REQUEST_DESELECT, + //Request to disable a channel - response will be a PROCESSING_STOP_NOTIFICATION + REQUEST_DISABLE, + //Request to enable a channel - response will be a PROCESSING_START_NOTIFICATION + REQUEST_ENABLE, + //Request to select the channel + REQUEST_SELECT; + } } diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelGrantEvent.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelGrantEvent.java new file mode 100644 index 00000000..1666df2d --- /dev/null +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelGrantEvent.java @@ -0,0 +1,64 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.controller.channel; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.IdentifierCollection; + +/** + * Channel grant event with a channel descriptor that identifies the channel that is being created + */ +public class ChannelGrantEvent extends ChannelEvent +{ + private IChannelDescriptor mChannelDescriptor; + private IdentifierCollection mIdentifierCollection; + + /** + * Constructs a channel grant event + * @param channel with setup/configuration details + * @param event to convey for the channel + * @param channelDescriptor for the channel that is to be created + * @param identifierCollection containing identifiers to preload into the channel + */ + public ChannelGrantEvent(Channel channel, Event event, IChannelDescriptor channelDescriptor, + IdentifierCollection identifierCollection) + { + super(channel, event); + mChannelDescriptor = channelDescriptor; + mIdentifierCollection = identifierCollection; + } + + /** + * Channel descriptor that contains uplink and downlink information + */ + public IChannelDescriptor getChannelDescriptor() + { + return mChannelDescriptor; + } + + /** + * Identifier collection to use in preloading the channel state for the allocated channel + */ + public IdentifierCollection getIdentifierCollection() + { + return mIdentifierCollection; + } +} diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelModel.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelModel.java index 30939c9c..3dedb42a 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelModel.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelModel.java @@ -21,6 +21,8 @@ package io.github.dsheirer.controller.channel; import io.github.dsheirer.controller.channel.Channel.ChannelType; import io.github.dsheirer.controller.channel.ChannelEvent.Event; import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.sample.Broadcaster; +import io.github.dsheirer.sample.Listener; import javax.swing.table.AbstractTableModel; import java.util.ArrayList; @@ -32,7 +34,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * Channel Model */ -public class ChannelModel extends AbstractTableModel implements IChannelEventBroadcaster +public class ChannelModel extends AbstractTableModel implements Listener { private static final long serialVersionUID = 1L; @@ -51,7 +53,7 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro private List mChannels = new CopyOnWriteArrayList<>(); private List mTrafficChannels = new CopyOnWriteArrayList<>(); - private List mListeners = new CopyOnWriteArrayList<>(); + private Broadcaster mChannelEventBroadcaster = new Broadcaster(); public ChannelModel() { @@ -75,25 +77,6 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro return null; } - /** - * Returns the channel identified by the id argument - */ - public Channel getChannelFromChannelID(Integer id) - { - if(id != null) - { - for(Channel channel: mChannels) - { - if(channel.getChannelID() == id) - { - return channel; - } - } - } - - return null; - } - /** * Returns a list of unique system values from across the channel set */ @@ -144,24 +127,30 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro /** * Broadcasts the channel event to all registered listeners */ - @Override - public void broadcast(ChannelEvent event) + public void receive(ChannelEvent event) { - for(ChannelEventListener listener : mListeners) - { - listener.channelChanged(event); - } - if(event.getChannel().getChannelType() == ChannelType.STANDARD) { + int index; switch(event.getEvent()) { case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_SELECTION_CHANGE: + index = mChannels.indexOf(event.getChannel()); + if(index > 0) + { + fireTableRowsUpdated(index, index); + } + //rebroadcast this event to any listeners + mChannelEventBroadcaster.broadcast(event); + break; case NOTIFICATION_PROCESSING_START: case NOTIFICATION_PROCESSING_STOP: - case NOTIFICATION_SELECTION_CHANGE: - int index = mChannels.indexOf(event.getChannel()); - fireTableRowsUpdated(index, index); + index = mChannels.indexOf(event.getChannel()); + if(index > 0) + { + fireTableRowsUpdated(index, index); + } break; default: break; @@ -210,7 +199,7 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro break; } - broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD)); + mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD)); return index; } @@ -241,7 +230,7 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro break; } - broadcast(new ChannelEvent(channel, Event.NOTIFICATION_DELETE)); + mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_DELETE)); } } @@ -317,14 +306,20 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro return channels; } - public void addListener(ChannelEventListener listener) + /** + * Adds a listener to receive channel events + */ + public void addListener(Listener listener) { - mListeners.add(listener); + mChannelEventBroadcaster.addListener(listener); } - public void removeListener(ChannelEventListener listener) + /** + * Removes listener from receiving channel events + */ + public void removeListener(Listener listener) { - mListeners.remove(listener); + mChannelEventBroadcaster.removeListener(listener); } //Table Model Interface Methods - standard channels only diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java index 8d01a8d0..2b7a1809 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,72 +15,86 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; +import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.channel.metadata.ChannelMetadataModel; import io.github.dsheirer.controller.channel.map.ChannelMapModel; import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.IdentifierUpdateNotification; +import io.github.dsheirer.identifier.configuration.ChannelDescriptorConfigurationIdentifier; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.Module; import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.module.decode.DecoderFactory; -import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.module.decode.event.IDecodeEvent; import io.github.dsheirer.module.decode.event.MessageActivityModel; import io.github.dsheirer.module.log.EventLogManager; import io.github.dsheirer.record.RecorderManager; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.binary.BinaryRecorder; +import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; import io.github.dsheirer.source.Source; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.SourceManager; -import io.github.dsheirer.source.SourceType; -import io.github.dsheirer.source.config.SourceConfigTuner; -import io.github.dsheirer.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; -public class ChannelProcessingManager implements ChannelEventListener +/** + * Channel processing manager handles all starting and stopping of channel decoding. A processing chain is created + * for each channel that is enabled. The processing chain contains all of the components needed to decode a specific + * channel and protocol along with all logging and baseband or bitstream recording. Audio recording is handled outside + * of this class by the RecorderManager. + */ +public class ChannelProcessingManager implements Listener { private final static Logger mLog = LoggerFactory.getLogger(ChannelProcessingManager.class); - - private Map mProcessingChains = new HashMap<>(); + private static final String TUNER_UNAVAILABLE_DESCRIPTION = "TUNER UNAVAILABLE"; + private Map mProcessingChains = new HashMap<>(); private List> mAudioPacketListeners = new CopyOnWriteArrayList<>(); - private List> mMessageListeners = new CopyOnWriteArrayList<>(); + private List> mDecodeEventListeners = new CopyOnWriteArrayList<>(); + private Broadcaster mChannelEventBroadcaster = new Broadcaster(); - private ChannelModel mChannelModel; private ChannelMapModel mChannelMapModel; - private ChannelMetadataModel mChannelMetadataModel = new ChannelMetadataModel(); - private AliasModel mAliasModel; + private ChannelMetadataModel mChannelMetadataModel; private EventLogManager mEventLogManager; private RecorderManager mRecorderManager; private SourceManager mSourceManager; + private AliasModel mAliasModel; - public ChannelProcessingManager(ChannelModel channelModel, - ChannelMapModel channelMapModel, - AliasModel aliasModel, - EventLogManager eventLogManager, - RecorderManager recorderManager, - SourceManager sourceManager) + /** + * Constructs the channel processing manager + * + * @param channelMapModel containing channel maps defined by the user + * @param eventLogManager for adding event loggers to channels + * @param recorderManager for receiving audio packets produced by the channel + * @param sourceManager for obtaining a tuner channel source for the channel + * @param aliasModel for aliasing of identifiers produced by the channel + */ + public ChannelProcessingManager(ChannelMapModel channelMapModel, EventLogManager eventLogManager, + RecorderManager recorderManager, SourceManager sourceManager, AliasModel aliasModel) { - mChannelModel = channelModel; mChannelMapModel = channelMapModel; - mAliasModel = aliasModel; mEventLogManager = eventLogManager; mRecorderManager = recorderManager; mSourceManager = sourceManager; + mAliasModel = aliasModel; + mChannelMetadataModel = new ChannelMetadataModel(); } /** @@ -90,22 +105,13 @@ public class ChannelProcessingManager implements ChannelEventListener return mChannelMetadataModel; } - /** - * Channel model - */ - public ChannelModel getChannelModel() - { - return mChannelModel; - } - /** * Indicates if a processing chain is constructed for the channel and that * the processing chain is currently processing. */ private boolean isProcessing(Channel channel) { - return mProcessingChains.containsKey(channel.getChannelID()) && - mProcessingChains.get(channel.getChannelID()).isProcessing(); + return mProcessingChains.containsKey(channel) && mProcessingChains.get(channel).isProcessing(); } /** @@ -114,27 +120,20 @@ public class ChannelProcessingManager implements ChannelEventListener */ public ProcessingChain getProcessingChain(Channel channel) { - return mProcessingChains.get(channel.getChannelID()); + return mProcessingChains.get(channel); } /** * Returns the channel associated with the processing chain + * * @param processingChain * @return channel associated with the processing chain or null */ public Channel getChannel(ProcessingChain processingChain) - { - return mChannelModel.getChannelFromChannelID(getChannelID(processingChain)); - } - - /** - * Returns the channel ID associated with the processing chain - */ - private Integer getChannelID(ProcessingChain processingChain) { if(processingChain != null) { - for(Map.Entry entry: mProcessingChains.entrySet()) + for(Map.Entry entry : mProcessingChains.entrySet()) { if(entry.getValue() == processingChain) { @@ -146,16 +145,20 @@ public class ChannelProcessingManager implements ChannelEventListener return null; } + /** + * Primary method for receiving requests to start and stop a channel + * + * @param event that requests either enable/start or disable/stop a channel. + */ @Override - public synchronized void channelChanged(ChannelEvent event) + public synchronized void receive(ChannelEvent event) { Channel channel = event.getChannel(); switch(event.getEvent()) { case REQUEST_ENABLE: - if(!mProcessingChains.containsKey(channel.getChannelID()) || - !mProcessingChains.get(channel.getChannelID()).isProcessing()) + if(!isProcessing(channel)) { startProcessing(event); } @@ -195,11 +198,16 @@ public class ChannelProcessingManager implements ChannelEventListener } } + /** + * Starts a channel/processing chain + * + * @param event that requested the channel start + */ private void startProcessing(ChannelEvent event) { Channel channel = event.getChannel(); - ProcessingChain processingChain = mProcessingChains.get(channel.getChannelID()); + ProcessingChain processingChain = mProcessingChains.get(channel); //If we're already processing, ignore the request if(processingChain != null && processingChain.isProcessing()) @@ -224,71 +232,48 @@ public class ChannelProcessingManager implements ChannelEventListener { channel.setProcessing(false); - mChannelModel.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_START_PROCESSING_REJECTED)); + mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_START_REJECTED, + TUNER_UNAVAILABLE_DESCRIPTION)); return; } if(processingChain == null) { - processingChain = new ProcessingChain(channel.getChannelType()); + processingChain = new ProcessingChain(channel, mAliasModel); + mChannelEventBroadcaster.addListener(processingChain); - /* Register global listeners */ + /* Register global listeners */ for(Listener listener : mAudioPacketListeners) { processingChain.addAudioPacketListener(listener); } - for(Listener listener : mMessageListeners) + for(Listener listener : mDecodeEventListeners) { - processingChain.addMessageListener(listener); + processingChain.addDecodeEventListener(listener); } + //Register this manager to receive channel events from traffic channel manager modules within + //the processing chain + processingChain.addChannelEventListener(this); + //Register channel to receive frequency correction events to show in the spectral display (hack!) processingChain.addFrequencyChangeListener(channel); - /* Processing Modules */ - List modules = DecoderFactory.getModules(mChannelModel, mChannelMapModel, this, - mAliasModel, channel, processingChain.getChannelState().getMutableMetadata()); + /* Processing Modules */ + List modules = DecoderFactory.getModules(mChannelMapModel, channel, mAliasModel); processingChain.addModules(modules); - /* Setup message activity model with filtering */ - FilterSet messageFilter = DecoderFactory.getMessageFilters(modules); + /* Setup message activity model with filtering */ + FilterSet messageFilter = DecoderFactory.getMessageFilters(modules); MessageActivityModel messageModel = new MessageActivityModel(messageFilter); processingChain.setMessageActivityModel(messageModel); } - //Set the recordable flag to true if the user has requested recording. The metadata class can still - //override recordability if any of the aliased values has 'Do Not Record' alias identifier. - boolean recordable = channel.getRecordConfiguration() != null && - channel.getRecordConfiguration().getRecorders().contains(RecorderType.AUDIO); - - processingChain.getChannelState().getMutableMetadata().setRecordable(recordable); - - //Inject channel metadata that will be inserted into audio packets for recorder manager and streaming - processingChain.getChannelState().getMutableMetadata().receive( - new AttributeChangeRequest(Attribute.CHANNEL_CONFIGURATION_SYSTEM, channel.getSystem())); - processingChain.getChannelState().getMutableMetadata().receive( - new AttributeChangeRequest(Attribute.CHANNEL_CONFIGURATION_SITE, channel.getSite())); - processingChain.getChannelState().getMutableMetadata().receive( - new AttributeChangeRequest(Attribute.CHANNEL_CONFIGURATION_NAME, channel.getName())); - - if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) - { - long frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency(); - - processingChain.getChannelState().getMutableMetadata().receive( - new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, frequency)); - } - - processingChain.getChannelState().getMutableMetadata().receive( - new AttributeChangeRequest(Attribute.PRIMARY_DECODER_TYPE, - channel.getDecodeConfiguration().getDecoderType())); - /* Setup event logging */ - String cleanedChannelName = StringUtils.replaceIllegalCharacters(channel.getName()); - List loggers = mEventLogManager.getLoggers(channel.getEventLogConfiguration(), cleanedChannelName); + List loggers = mEventLogManager.getLoggers(channel); if(!loggers.isEmpty()) { @@ -319,7 +304,7 @@ public class ChannelProcessingManager implements ChannelEventListener channel.getChannelType() == Channel.ChannelType.STANDARD)) { processingChain.addModule(new BinaryRecorder(mRecorderManager.getRecordingBasePath(), - channel.toString())); + channel.toString(), channel.getDecodeConfiguration().getDecoderType().getProtocol())); } /* Add traffic channel decoded bit stream recorder */ @@ -327,42 +312,62 @@ public class ChannelProcessingManager implements ChannelEventListener channel.getChannelType() == Channel.ChannelType.TRAFFIC) { processingChain.addModule(new BinaryRecorder(mRecorderManager.getRecordingBasePath(), - channel.toString())); + channel.toString(), channel.getDecodeConfiguration().getDecoderType().getProtocol())); } } } processingChain.setSource(source); - if(event instanceof TrafficChannelEvent) + //Inject the channel identifier for traffic channels and preload user identifiers + if(channel.isTrafficChannel() && event instanceof ChannelGrantEvent) { - TrafficChannelEvent trafficChannelEvent = (TrafficChannelEvent) event; + ChannelGrantEvent channelGrantEvent = (ChannelGrantEvent)event; + IChannelDescriptor channelDescriptor = channelGrantEvent.getChannelDescriptor(); - processingChain.getChannelState().configureAsTrafficChannel( - trafficChannelEvent.getTrafficChannelManager(), - trafficChannelEvent.getCallEvent()); + if(channelDescriptor != null) + { + ChannelDescriptorConfigurationIdentifier identifier = new ChannelDescriptorConfigurationIdentifier(channelDescriptor); + IdentifierUpdateNotification notification = new IdentifierUpdateNotification(identifier, + IdentifierUpdateNotification.Operation.ADD); + processingChain.getChannelState().updateChannelStateIdentifiers(notification); + } + + IdentifierCollection identifierCollection = channelGrantEvent.getIdentifierCollection(); + + for(Identifier userIdentifier : identifierCollection.getIdentifiers(IdentifierClass.USER)) + { + IdentifierUpdateNotification notification = new IdentifierUpdateNotification(userIdentifier, + IdentifierUpdateNotification.Operation.ADD); + processingChain.getChannelState().updateChannelStateIdentifiers(notification); + } } processingChain.start(); - - getChannelMetadataModel().add(processingChain.getChannelState().getMutableMetadata(), channel); - channel.setProcessing(true); - mProcessingChains.put(channel.getChannelID(), processingChain); + getChannelMetadataModel().add(processingChain.getChannelState().getChannelMetadata(), channel); - mChannelModel.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_START)); + mProcessingChains.put(channel, processingChain); + + mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_START)); } + /** + * Stops the channel/processing chain. + * + * @param channel to stop + * @param remove set to true to remove the associated processing chain. + */ private void stopProcessing(Channel channel, boolean remove) { channel.setProcessing(false); - if(mProcessingChains.containsKey(channel.getChannelID())) + if(mProcessingChains.containsKey(channel)) { - ProcessingChain processingChain = mProcessingChains.get(channel.getChannelID()); + ProcessingChain processingChain = mProcessingChains.get(channel); - getChannelMetadataModel().remove(processingChain.getChannelState().getMutableMetadata()); + getChannelMetadataModel().remove(processingChain.getChannelState().getChannelMetadata()); processingChain.stop(); @@ -374,12 +379,12 @@ public class ChannelProcessingManager implements ChannelEventListener processingChain.removeFrequencyChangeListener(channel); channel.resetFrequencyCorrection(); - mChannelModel.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_STOP)); + mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_STOP)); if(remove) { - mProcessingChains.remove(channel.getChannelID()); - + mChannelEventBroadcaster.removeListener(processingChain); + mProcessingChains.remove(channel); processingChain.dispose(); } } @@ -391,13 +396,13 @@ public class ChannelProcessingManager implements ChannelEventListener public void shutdown() { mLog.debug("Stopping Channels ..."); - for(Channel channel: mChannelModel.getChannels()) + + List channelsToStop = new ArrayList<>(mProcessingChains.keySet()); + + for(Channel channel : channelsToStop) { - if(channel.isProcessing()) - { - mLog.debug("Stopping channel: " + channel.toString()); - stopProcessing(channel, true); - } + mLog.debug("Stopping channel: " + channel.toString()); + stopProcessing(channel, true); } } @@ -422,16 +427,32 @@ public class ChannelProcessingManager implements ChannelEventListener * Adds a message listener that will be added to all channels to receive * any messages. */ - public void addMessageListener(Listener listener) + public void addDecodeEventListener(Listener listener) { - mMessageListeners.add(listener); + mDecodeEventListeners.add(listener); } /** * Removes a message listener. */ - public void removeMessageListener(Listener listener) + public void removeDecodeEventListener(Listener listener) { - mMessageListeners.remove(listener); + mDecodeEventListeners.remove(listener); + } + + /** + * Adds a listener to receive channel events from this manager + */ + public void addChannelEventListener(Listener listener) + { + mChannelEventBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving channel events from this manager + */ + public void removeChannelEventListener(Listener listener) + { + mChannelEventBroadcaster.removeListener(listener); } } diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelSelectionManager.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelSelectionManager.java index 05c9fd4a..bc64ae91 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelSelectionManager.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelSelectionManager.java @@ -1,69 +1,69 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; -public class ChannelSelectionManager implements ChannelEventListener +import io.github.dsheirer.sample.Listener; + +public class ChannelSelectionManager implements Listener { - private IChannelEventBroadcaster mChannelEventBroadcaster; - private Channel mSelectedChannel = null; + private Listener mChannelEventListener; + private Channel mSelectedChannel = null; - /** - * Manages channel selection state to ensure that only one channel is ever - * in a selected state. - */ - public ChannelSelectionManager( IChannelEventBroadcaster broadcaster ) - { - mChannelEventBroadcaster = broadcaster; - } + /** + * Manages channel selection state to ensure that only one channel is ever + * in a selected state. + */ + public ChannelSelectionManager(Listener listener) + { + mChannelEventListener = listener; + } - @Override - public void channelChanged( ChannelEvent event ) - { - switch( event.getEvent() ) - { - case REQUEST_SELECT: - if( mSelectedChannel != null ) - { - mSelectedChannel.setSelected( false ); - mChannelEventBroadcaster.broadcast( new ChannelEvent( mSelectedChannel, - ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE ) ); - mSelectedChannel = null; - } - - mSelectedChannel = event.getChannel(); - mSelectedChannel.setSelected( true ); - mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(), - ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE ) ); - break; - case NOTIFICATION_PROCESSING_STOP: - case REQUEST_DESELECT: - if( mSelectedChannel != null && - mSelectedChannel == event.getChannel() ) - { - mSelectedChannel.setSelected( false ); - mSelectedChannel = null; - mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(), - ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE ) ); - } - break; - default: - break; - } - } + @Override + public void receive(ChannelEvent event) + { + switch(event.getEvent()) + { + case REQUEST_SELECT: + if(mSelectedChannel != null) + { + mSelectedChannel.setSelected(false); + mChannelEventListener.receive(new ChannelEvent(mSelectedChannel, ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE)); + mSelectedChannel = null; + } + mSelectedChannel = event.getChannel(); + mSelectedChannel.setSelected(true); + mChannelEventListener.receive(new ChannelEvent(event.getChannel(), ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE)); + break; + case NOTIFICATION_PROCESSING_STOP: + case REQUEST_DESELECT: + if(mSelectedChannel != null && + mSelectedChannel == event.getChannel()) + { + mSelectedChannel.setSelected(false); + mSelectedChannel = null; + mChannelEventListener.receive(new ChannelEvent(event.getChannel(), ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE)); + } + break; + default: + break; + } + } } diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelUtils.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelUtils.java index 69cff222..5cc173d3 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelUtils.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelUtils.java @@ -1,3 +1,23 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.controller.channel; import io.github.dsheirer.channel.state.DecoderState; @@ -14,104 +34,101 @@ import java.awt.event.ActionListener; public class ChannelUtils { - /** - * Creates a context menu for the channel argument - */ - public static JMenu getContextMenu( final ChannelModel channelModel, - final ChannelProcessingManager channelProcessingManager, - final Channel channel, - final Component anchor ) - { - if( channel != null ) - { - JMenu menu = new JMenu( "Channel: " + channel.getName() ); - - if( channel.isProcessing() ) - { - JMenuItem disable = new JMenuItem( "Disable" ); - disable.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - channelModel.broadcast( - new ChannelEvent( channel, ChannelEvent.Event.REQUEST_DISABLE ) ); - } - } ); - - menu.add( disable ); - - menu.add( new JSeparator() ); + /** + * Creates a context menu for the channel argument + */ + public static JMenu getContextMenu(final ChannelModel channelModel, + final ChannelProcessingManager channelProcessingManager, + final Channel channel, + final Component anchor) + { + if(channel != null) + { + JMenu menu = new JMenu("Channel: " + channel.getName()); - JMenuItem actySummaryItem = - new JMenuItem( "Activity Summary" ); + if(channel.isProcessing()) + { + JMenuItem disable = new JMenuItem("Disable"); + disable.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + channelProcessingManager.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_DISABLE)); + } + }); + + menu.add(disable); + + menu.add(new JSeparator()); + + JMenuItem actySummaryItem = + new JMenuItem("Activity Summary"); + + actySummaryItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + StringBuilder sb = new StringBuilder(); + + ProcessingChain chain = channelProcessingManager.getProcessingChain(channel); + + if(chain != null) + { + for(DecoderState decoderState : chain.getDecoderStates()) + { + sb.append(decoderState.getActivitySummary()); + } + } + + new ActivitySummaryFrame(sb.toString(), anchor); + } + }); + + menu.add(actySummaryItem); + } + else + { + JMenuItem enable = new JMenuItem("Enable"); + enable.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + channelProcessingManager.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE)); + } + }); + + menu.add(enable); + } + + menu.add(new JSeparator()); + + JMenuItem deleteItem = new JMenuItem("Delete"); + deleteItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + int response = JOptionPane.showConfirmDialog(anchor, + "Do you want to delete channel " + channel.getName() + "?", + "Are you sure?", JOptionPane.YES_NO_CANCEL_OPTION); + + if(response == JOptionPane.YES_OPTION) + { + channelModel.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_DELETE)); + } + } + }); + + menu.add(deleteItem); + + return menu; + + } + + return null; + } - actySummaryItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - StringBuilder sb = new StringBuilder(); - - ProcessingChain chain = channelProcessingManager.getProcessingChain( channel ); - - if( chain != null ) - { - for( DecoderState decoderState: chain.getDecoderStates() ) - { - sb.append( decoderState.getActivitySummary() ); - } - } - - new ActivitySummaryFrame( sb.toString(), anchor ); - } - } ); - - menu.add( actySummaryItem ); - } - else - { - JMenuItem enable = new JMenuItem( "Enable" ); - enable.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - channelModel.broadcast( - new ChannelEvent( channel, ChannelEvent.Event.REQUEST_ENABLE ) ); - } - } ); - - menu.add( enable ); - } - - menu.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int response = JOptionPane.showConfirmDialog( anchor, - "Do you want to delete channel " + channel.getName() + "?", - "Are you sure?", JOptionPane.YES_NO_CANCEL_OPTION ); - - if( response == JOptionPane.YES_OPTION ) - { - channelModel.broadcast( - new ChannelEvent( channel, ChannelEvent.Event.REQUEST_DELETE ) ); - } - } - } ); - - menu.add( deleteItem ); - - return menu; - - } - - return null; - } - } diff --git a/src/main/java/io/github/dsheirer/controller/channel/IChannelEventBroadcaster.java b/src/main/java/io/github/dsheirer/controller/channel/IChannelEventBroadcaster.java deleted file mode 100644 index 2b365072..00000000 --- a/src/main/java/io/github/dsheirer/controller/channel/IChannelEventBroadcaster.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2016 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.controller.channel; - -/** - * Provides channel event broadcasting capability. - */ -public interface IChannelEventBroadcaster -{ - /** - * Broadcasts the channel event - */ - public void broadcast( ChannelEvent event ); -} diff --git a/src/main/java/io/github/dsheirer/controller/channel/IChannelEventProvider.java b/src/main/java/io/github/dsheirer/controller/channel/IChannelEventProvider.java index d6acb8ad..a465a5fc 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/IChannelEventProvider.java +++ b/src/main/java/io/github/dsheirer/controller/channel/IChannelEventProvider.java @@ -4,6 +4,6 @@ import io.github.dsheirer.sample.Listener; public interface IChannelEventProvider { - public void setChannelEventListener( Listener listener ); - public void removeChannelEventListener(); + void setChannelEventListener( Listener listener ); + void removeChannelEventListener(); } diff --git a/src/main/java/io/github/dsheirer/controller/channel/TrafficChannelEvent.java b/src/main/java/io/github/dsheirer/controller/channel/TrafficChannelEvent.java deleted file mode 100644 index f00ddcc4..00000000 --- a/src/main/java/io/github/dsheirer/controller/channel/TrafficChannelEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.dsheirer.controller.channel; - -import io.github.dsheirer.channel.traffic.TrafficChannelManager; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class TrafficChannelEvent extends ChannelEvent -{ - private TrafficChannelManager mTrafficChannelManager; - private CallEvent mCallEvent; - - /** - * Call event with traffic channel manager for call back, and original creation event. - */ - public TrafficChannelEvent( TrafficChannelManager trafficChannelManager, - Channel channel, - Event event, - CallEvent callEvent ) - { - super( channel, event ); - - mTrafficChannelManager = trafficChannelManager; - mCallEvent = callEvent; - } - - public TrafficChannelManager getTrafficChannelManager() - { - return mTrafficChannelManager; - } - - public CallEvent getCallEvent() - { - return mCallEvent; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/NRZDecoder.java b/src/main/java/io/github/dsheirer/dsp/NRZDecoder.java index a93e9c01..10adc784 100644 --- a/src/main/java/io/github/dsheirer/dsp/NRZDecoder.java +++ b/src/main/java/io/github/dsheirer/dsp/NRZDecoder.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.dsp; import io.github.dsheirer.bits.IBinarySymbolProcessor; @@ -52,13 +56,13 @@ public class NRZDecoder implements IBinarySymbolProcessor } @Override - public void receive(boolean symbol) + public void process(boolean symbol) { boolean result = mPrevious ^ symbol; if(mListener != null) { - mListener.receive(mMode ? result : !result); + mListener.process(mMode ? result : !result); } mPrevious = result; diff --git a/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java b/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java index 1602986a..f2ca56bc 100644 --- a/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java +++ b/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.dsp.afsk; import io.github.dsheirer.bits.IBinarySymbolProcessor; @@ -119,7 +123,7 @@ public class AFSK1200Decoder implements Listener { if(mBinarySymbolProcessor != null) { - mBinarySymbolProcessor.receive(mNormalOutput ? symbol : !symbol); + mBinarySymbolProcessor.process(mNormalOutput ? symbol : !symbol); } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java index 75835c57..2ffe2587 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java @@ -33,10 +33,11 @@ public class ContinuousBufferProcessor implements Listener { private final static Logger mLog = LoggerFactory.getLogger(ContinuousBufferProcessor.class); - private OverflowableTransferQueue mQueue; + protected OverflowableTransferQueue mQueue; private Listener> mListener; private ScheduledFuture mScheduledFuture; private AtomicBoolean mRunning = new AtomicBoolean(); + private long mProcessingPeriod = 5; //milliseconds /** * Scheduled Buffer Processor combines an internal overflowable buffer with a scheduled runnable processing task @@ -57,6 +58,24 @@ public class ContinuousBufferProcessor implements Listener this(new OverflowableTransferQueue<>(maximumSize, resetThreshold)); } + /** + * Listener to receive the queued buffers each time this processor runs. + */ + protected Listener> getListener() + { + return mListener; + } + + /** + * Sets the processing periodicity or run interval for how often the buffer queue is processed. + * + * @param milliseconds (default = 5 milliseconds) + */ + public void setProcessingPeriod(long milliseconds) + { + mProcessingPeriod = milliseconds; + } + /** * Scheduled Buffer Processor combines an internal overflowable buffer with a scheduled runnable processing task * for periodically distributing internally queued elements to the registered listener. This processor provides @@ -115,7 +134,7 @@ public class ContinuousBufferProcessor implements Listener { if(mRunning.compareAndSet(false, true)) { - mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new Processor(), 0, 5, TimeUnit.MILLISECONDS); + mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new Processor(), 0, mProcessingPeriod, TimeUnit.MILLISECONDS); } } @@ -157,7 +176,7 @@ public class ContinuousBufferProcessor implements Listener /** * Distributes queued buffers to the listener */ - private void process() + protected void process() { List buffers = new ArrayList<>(); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousReusableBufferProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousReusableBufferProcessor.java index 57f9f2fb..9f3b9cb9 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousReusableBufferProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousReusableBufferProcessor.java @@ -17,9 +17,16 @@ package io.github.dsheirer.dsp.filter.channelizer; import io.github.dsheirer.sample.buffer.AbstractReusableBuffer; import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; public class ContinuousReusableBufferProcessor extends ContinuousBufferProcessor { + private final static Logger mLog = LoggerFactory.getLogger(ContinuousReusableBufferProcessor.class); + /** * Scheduled Reusable Buffer Processor combines an internal overflowable buffer with a scheduled runnable processing * task for periodically distributing internally queued elements to the registered listener. This processor @@ -38,4 +45,44 @@ public class ContinuousReusableBufferProcessor { super(new OverflowableReusableBufferTransferQueue(maximumSize, resetThreshold)); } + + /** + * Distributes queued buffers to the listener + */ + @Override + protected void process() + { + List buffers = new ArrayList<>(); + + mQueue.drainTo(buffers); + + try + { + if(getListener() != null) + { + getListener().receive(buffers); + } + + buffers.clear(); + } + catch(Throwable throwable) + { + mLog.error("Error while dispatching buffers to listener. Performing buffer user count cleanup", throwable); + + if(!buffers.isEmpty()) + { + for(T buffer: buffers) + { + try + { + buffer.decrementUserCount(); + } + catch(IllegalStateException ise) + { + mLog.error("Error while performing user count cleanup on reusable buffers."); + } + } + } + } + } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java index a21a244e..de590798 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.dsp.filter.channelizer; import io.github.dsheirer.dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor; @@ -28,15 +32,13 @@ import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.channel.ChannelSpecification; import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; public class PolyphaseChannelSource extends TunerChannelSource { - private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class); +// private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class); private ReusableComplexBufferAssembler mReusableComplexBufferAssembler; private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor; diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SampleTimestampManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SampleTimestampManager.java index 38c8a943..39c02649 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SampleTimestampManager.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SampleTimestampManager.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.dsp.filter.channelizer; public class SampleTimestampManager @@ -29,6 +33,7 @@ public class SampleTimestampManager */ public SampleTimestampManager(double sampleRate) { + setSampleRate(sampleRate); } /** diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java index 335e957d..95862798 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -80,7 +80,6 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP if(mChannelResultsQueue != null) { mChannelResultsQueue.dispose(); - mChannelResultsQueue = null; } } diff --git a/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java b/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java index cda51b67..c502341d 100644 --- a/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java +++ b/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.dsp.fsk; import io.github.dsheirer.bits.MessageFramer; @@ -129,9 +133,9 @@ public class LTRDecoder implements Listener, ISyncDetectLis * synchronization has been achieved. */ @Override - public void syncDetected() + public void syncDetected(int bitErrors) { - mSynchronizationMonitor.syncDetected(); + mSynchronizationMonitor.syncDetected(bitErrors); } @Override @@ -164,7 +168,7 @@ public class LTRDecoder implements Listener, ISyncDetectLis { if(mMessageFramer != null) { - mMessageFramer.receive(mSampleBuffer.getSymbol()); + mMessageFramer.process(mSampleBuffer.getSymbol()); } mSampleBuffer.resetAndAdjust(-mTimingErrorDetector.getError()); diff --git a/src/main/java/io/github/dsheirer/dsp/fsk/SynchronizationMonitor.java b/src/main/java/io/github/dsheirer/dsp/fsk/SynchronizationMonitor.java index 7d9746b7..f8c514c5 100644 --- a/src/main/java/io/github/dsheirer/dsp/fsk/SynchronizationMonitor.java +++ b/src/main/java/io/github/dsheirer/dsp/fsk/SynchronizationMonitor.java @@ -121,7 +121,7 @@ public class SynchronizationMonitor implements ISyncDetectListener * sync word in the symbol stream. */ @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mSymbolCount = 0; mSyncCount++; diff --git a/src/main/java/io/github/dsheirer/dsp/psk/pll/AdaptivePLLGainMonitor.java b/src/main/java/io/github/dsheirer/dsp/psk/pll/AdaptivePLLGainMonitor.java index eb48da17..6b94d677 100644 --- a/src/main/java/io/github/dsheirer/dsp/psk/pll/AdaptivePLLGainMonitor.java +++ b/src/main/java/io/github/dsheirer/dsp/psk/pll/AdaptivePLLGainMonitor.java @@ -53,7 +53,7 @@ public class AdaptivePLLGainMonitor implements ISyncDetectListener, IFrequencyEr * Sync detection event. Updates the running sync count and updates the PLL gain level. */ @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mSyncCount++; update(); diff --git a/src/main/java/io/github/dsheirer/dsp/symbol/BinaryToByteBufferAssembler.java b/src/main/java/io/github/dsheirer/dsp/symbol/BinaryToByteBufferAssembler.java new file mode 100644 index 00000000..7a87c079 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/symbol/BinaryToByteBufferAssembler.java @@ -0,0 +1,120 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.dsp.symbol; + +import io.github.dsheirer.bits.IBinarySymbolProcessor; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider; +import io.github.dsheirer.sample.buffer.ReusableByteBuffer; +import io.github.dsheirer.sample.buffer.ReusableByteBufferQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Assembles reusable byte buffers from an incoming stream of boolean values. + */ +public class BinaryToByteBufferAssembler implements IBinarySymbolProcessor, IReusableByteBufferProvider +{ + private final static Logger mLog = LoggerFactory.getLogger(BinaryToByteBufferAssembler.class); + + private ReusableByteBufferQueue mBufferQueue = new ReusableByteBufferQueue("BinaryToByteBufferAssembler"); + private ReusableByteBuffer mCurrentBuffer; + private int mBufferPointer; + private int mBufferSize; + private byte mCurrentByte; + private int mBitCount; + private Listener mBufferListener; + + /** + * Constructs an assembler to produce reusable byte buffers of the specified size + * + * @param bufferSize + */ + public BinaryToByteBufferAssembler(int bufferSize) + { + mBufferSize = bufferSize; + getNextBuffer(); + } + + /** + * Broadcasts the current buffer to the registered listener and creates a new buffer, resetting + * the buffer pointer to zero so that new dibits can be loaded. + */ + private void getNextBuffer() + { + if(mCurrentBuffer != null && mBufferListener != null) + { + mBufferListener.receive(mCurrentBuffer); + } + + mCurrentBuffer = mBufferQueue.getBuffer(mBufferSize); + mBufferPointer = 0; + } + + @Override + public void process(boolean symbol) + { + mCurrentByte <<= 1; + + if(symbol) + { + mCurrentByte++; + } + + mBitCount++; + + if(mBitCount >= 8) + { + mCurrentBuffer.getBytes()[mBufferPointer++] = mCurrentByte; + mCurrentByte = 0; + mBitCount = 0; + + if(mBufferPointer >= mBufferSize) + { + getNextBuffer(); + } + } + + } + + /** + * Registers the listener to receive fully assembled byte buffers from this assembler. + */ + @Override + public void setBufferListener(Listener listener) + { + mBufferListener = listener; + } + + /** + * Removes the listener from receiving buffers from this assembler + */ + @Override + public void removeBufferListener(Listener listener) + { + mBufferListener = null; + } + + @Override + public boolean hasBufferListeners() + { + return mBufferListener != null; + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/symbol/Dibit.java b/src/main/java/io/github/dsheirer/dsp/symbol/Dibit.java index 8ec42ec7..d912109e 100644 --- a/src/main/java/io/github/dsheirer/dsp/symbol/Dibit.java +++ b/src/main/java/io/github/dsheirer/dsp/symbol/Dibit.java @@ -2,111 +2,146 @@ package io.github.dsheirer.dsp.symbol; public enum Dibit { - D01_PLUS_3( false, true, 1, 4 ), - D00_PLUS_1( false, false, 0, 0 ), - D10_MINUS_1( true, false, 2, 8 ), - D11_MINUS_3( true, true, 3, 12 ); - - private boolean mBit1; - private boolean mBit2; - private int mLowValue; - private int mHighValue; - - private Dibit( boolean bit1, boolean bit2, int lowValue, int highValue ) - { - mBit1 = bit1; - mBit2 = bit2; - mLowValue = lowValue; - mHighValue = highValue; - } + D01_PLUS_3(false, true, 1, 4), + D00_PLUS_1(false, false, 0, 0), + D10_MINUS_1(true, false, 2, 8), + D11_MINUS_3(true, true, 3, 12); - /** - * Allowable transition state 1 for trellis coded modulation. Indicates - * the dibit that differs from this dibit in the LSB bit position. - */ - public Dibit getAllowableTransition1() - { - switch( this ) - { - case D00_PLUS_1: - return D01_PLUS_3; - case D01_PLUS_3: - return D00_PLUS_1; - case D10_MINUS_1: - return D11_MINUS_3; - case D11_MINUS_3: - return D10_MINUS_1; - default: - /* We'll never get here */ - return D00_PLUS_1; - } - } - - /** - * Allowable transition state 2 for trellis coded modulation. Indicates - * the dibit that differs from this dibit in the MSB bit position. - */ - public Dibit getAllowableTransition2() - { - switch( this ) - { - case D00_PLUS_1: - return D10_MINUS_1; - case D01_PLUS_3: - return D11_MINUS_3; - case D10_MINUS_1: - return D00_PLUS_1; - case D11_MINUS_3: - return D01_PLUS_3; - default: - /* We'll never get here */ - return D00_PLUS_1; - } - } + private boolean mBit1; + private boolean mBit2; + private int mLowValue; + private int mHighValue; - /** - * Indicates if the trellis coded modulation dibit is allowed to transition - * to the dibit argument. - */ - public boolean isAllowableTransition( Dibit dibit ) - { - return dibit == getAllowableTransition1() || - dibit == getAllowableTransition2(); - } - - public boolean getBit1() - { - return mBit1; - } - - public boolean getBit2() - { - return mBit2; - } - - public int getLowValue() - { - return mLowValue; - } - - public int getHighValue() - { - return mHighValue; - } - - public static Dibit inverted( Dibit symbol ) - { - switch( symbol ) - { - case D10_MINUS_1: - return D11_MINUS_3; - case D11_MINUS_3: - return D01_PLUS_3; - case D00_PLUS_1: - return D10_MINUS_1; - case D01_PLUS_3: - default: - return D00_PLUS_1; - } - } + private Dibit(boolean bit1, boolean bit2, int lowValue, int highValue) + { + mBit1 = bit1; + mBit2 = bit2; + mLowValue = lowValue; + mHighValue = highValue; + } + + /** + * Allowable transition state 1 for trellis coded modulation. Indicates + * the dibit that differs from this dibit in the LSB bit position. + */ + public Dibit getAllowableTransition1() + { + switch(this) + { + case D00_PLUS_1: + return D01_PLUS_3; + case D01_PLUS_3: + return D00_PLUS_1; + case D10_MINUS_1: + return D11_MINUS_3; + case D11_MINUS_3: + return D10_MINUS_1; + default: + /* We'll never get here */ + return D00_PLUS_1; + } + } + + /** + * Allowable transition state 2 for trellis coded modulation. Indicates + * the dibit that differs from this dibit in the MSB bit position. + */ + public Dibit getAllowableTransition2() + { + switch(this) + { + case D00_PLUS_1: + return D10_MINUS_1; + case D01_PLUS_3: + return D11_MINUS_3; + case D10_MINUS_1: + return D00_PLUS_1; + case D11_MINUS_3: + return D01_PLUS_3; + default: + /* We'll never get here */ + return D00_PLUS_1; + } + } + + /** + * Indicates if the trellis coded modulation dibit is allowed to transition + * to the dibit argument. + */ + public boolean isAllowableTransition(Dibit dibit) + { + return dibit == getAllowableTransition1() || + dibit == getAllowableTransition2(); + } + + public boolean getBit1() + { + return mBit1; + } + + public boolean getBit2() + { + return mBit2; + } + + public int getLowValue() + { + return mLowValue; + } + + public int getHighValue() + { + return mHighValue; + } + + public static Dibit inverted(Dibit symbol) + { + switch(symbol) + { + case D10_MINUS_1: + return D11_MINUS_3; + case D11_MINUS_3: + return D01_PLUS_3; + case D00_PLUS_1: + return D10_MINUS_1; + case D01_PLUS_3: + default: + return D00_PLUS_1; + } + } + + /** + * Parses a dibit from the byte value at the specified index + * @param value to parse + * @param index index of dibit, 0 - 3, where 0 is the most significant dibit and 3 is the least. + * @return dibit parsed from the byte. + */ + public static Dibit parse(byte value, int index) + { + switch(value >> ((3 - index) * 2) & 0x3) + { + case 0: + return Dibit.D00_PLUS_1; + case 1: + return Dibit.D01_PLUS_3; + case 2: + return Dibit.D10_MINUS_1; + case 3: + return Dibit.D11_MINUS_3; + } + + return Dibit.D00_PLUS_1; + } + + public static void main(String[] args) + { + byte a = (byte)0x1B; + + Dibit d1 = Dibit.parse(a, 0); + Dibit d2 = Dibit.parse(a, 1); + Dibit d3 = Dibit.parse(a, 2); + Dibit d4 = Dibit.parse(a, 3); + + System.out.println("1: " + d1 + " 2:" + d2 + " 3:" + d3 + " 4:" + d4); + } } diff --git a/src/main/java/io/github/dsheirer/dsp/symbol/ISyncDetectListener.java b/src/main/java/io/github/dsheirer/dsp/symbol/ISyncDetectListener.java index 139deefb..e560c1e6 100644 --- a/src/main/java/io/github/dsheirer/dsp/symbol/ISyncDetectListener.java +++ b/src/main/java/io/github/dsheirer/dsp/symbol/ISyncDetectListener.java @@ -21,7 +21,16 @@ package io.github.dsheirer.dsp.symbol; */ public interface ISyncDetectListener { - void syncDetected(); + /** + * Indicates that a sync pattern has been detected. + * + * @param bitErrors count for soft sync matching to indicate the number of bit positions + * of the sequence that didn't fully match the sync pattern + */ + void syncDetected(int bitErrors); + /** + * Indicates that sync has been lost + */ void syncLost(); } diff --git a/src/main/java/io/github/dsheirer/edac/BerlekempMassey_63.java b/src/main/java/io/github/dsheirer/edac/BerlekempMassey_63.java index a21006e5..7467d651 100644 --- a/src/main/java/io/github/dsheirer/edac/BerlekempMassey_63.java +++ b/src/main/java/io/github/dsheirer/edac/BerlekempMassey_63.java @@ -55,7 +55,7 @@ public class BerlekempMassey_63 private static final int NN = 63; /* Hamming distance between codewords: NN - KK + 1 = 2 * TT + 1 */ - private static int KK; + private int KK; /* Maximum number of errors that can be corrected */ int TT; @@ -222,7 +222,7 @@ public class BerlekempMassey_63 /* put recd[i] into index form (ie as powers of alpha) */ for( int i = 0; i < NN; i++ ) { - output[ i ] = index_of[ input[ i ] ]; + output[ i ] = index_of[ input[ i ] ]; } /* first form the syndromes */ diff --git a/src/main/java/io/github/dsheirer/edac/CRC.java b/src/main/java/io/github/dsheirer/edac/CRC.java index 15c8cc26..15c22cb1 100644 --- a/src/main/java/io/github/dsheirer/edac/CRC.java +++ b/src/main/java/io/github/dsheirer/edac/CRC.java @@ -1,73 +1,82 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.edac; public enum CRC { - PASSED ( "*", "PASS" ), - PASSED_INV ( "#", "PASS INVERTED" ), - FAILED_CRC ( "f", "FAIL CRC" ), - FAILED_PARITY( "p", "FAIL PARITY" ), - CORRECTED ( "C", "CORRECTED" ), - UNKNOWN ( "-", "UNKNOWN"); + PASSED("*", "PASS", true), + PASSED_INV("#", "PASS INVERTED", true), + FAILED_CRC("f", "FAIL CRC", false), + FAILED_PARITY("p", "FAIL PARITY", false), + CORRECTED("C", "CORRECTED", true), + UNKNOWN("-", "UNKNOWN", false); - private String mAbbreviation; - private String mDisplayText; - - CRC( String abbreviation, String displayText ) - { - mAbbreviation = abbreviation; - mDisplayText = displayText; - } - - public String getAbbreviation() - { - return mAbbreviation; - } - - public String getDisplayText() - { - return mDisplayText; - } + private String mAbbreviation; + private String mDisplayText; + private boolean mPass; - public static String format( CRC[] checks ) - { - if( checks == null ) - { - return CRC.UNKNOWN.getAbbreviation(); - } - - StringBuilder sb = new StringBuilder(); - - for( int x = 0; x < checks.length; x++ ) - { - CRC check = checks[ x ]; - - if( check != null ) - { - sb.append( check.getAbbreviation() ); - } - else - { - sb.append( CRC.UNKNOWN.getAbbreviation() ); - } - } - - return sb.toString(); - } + CRC(String abbreviation, String displayText, boolean pass) + { + mAbbreviation = abbreviation; + mDisplayText = displayText; + mPass = pass; + } + + public String getAbbreviation() + { + return mAbbreviation; + } + + public String getDisplayText() + { + return mDisplayText; + } + + public boolean passes() + { + return mPass; + } + + public static String format(CRC[] checks) + { + if(checks == null) + { + return CRC.UNKNOWN.getAbbreviation(); + } + + StringBuilder sb = new StringBuilder(); + + for(int x = 0; x < checks.length; x++) + { + CRC check = checks[x]; + + if(check != null) + { + sb.append(check.getAbbreviation()); + } + else + { + sb.append(CRC.UNKNOWN.getAbbreviation()); + } + } + + return sb.toString(); + } } diff --git a/src/main/java/io/github/dsheirer/edac/CRCP25.java b/src/main/java/io/github/dsheirer/edac/CRCP25.java index f98c5a74..a5b9c389 100644 --- a/src/main/java/io/github/dsheirer/edac/CRCP25.java +++ b/src/main/java/io/github/dsheirer/edac/CRCP25.java @@ -1,23 +1,22 @@ /******************************************************************************* - * 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 + * sdr-trunk + * 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 + * ******************************************************************************/ package io.github.dsheirer.edac; import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,445 +25,485 @@ import org.slf4j.LoggerFactory; */ public class CRCP25 { - private final static Logger mLog = LoggerFactory.getLogger( CRCP25.class ); - - /** - * CRC-CCITT 16-bit checksums for a message length of 80 bits plus 16 - * additional checksums representing CRC checksum bit errors - * - * Generated by: - * CRCUtil.generate( 80, 16, 0x11021l, 0xFFFFl, true ); - */ - public static final int[] CCITT_80_CHECKSUMS = new int[] - { - 0x1BCB, 0x8DE5, 0xC6F2, 0x6B69, 0xB5B4, 0x52CA, 0x2175, 0x90BA, 0x404D, - 0xA026, 0x5803, 0xAC01, 0xD600, 0x6310, 0x3998, 0x14DC, 0x27E, 0x92F, - 0x8497, 0xC24B, 0xE125, 0xF092, 0x7059, 0xB82C, 0x5406, 0x2213, 0x9109, - 0xC884, 0x6C52, 0x3E39, 0x9F1C, 0x479E, 0x2BDF, 0x95EF, 0xCAF7, 0xE57B, - 0xF2BD, 0xF95E, 0x74BF, 0xBA5F, 0xDD2F, 0xEE97, 0xF74B, 0xFBA5, 0xFDD2, - 0x76F9, 0xBB7C, 0x55AE, 0x22C7, 0x9163, 0xC8B1, 0xE458, 0x7A3C, 0x350E, - 0x1297, 0x894B, 0xC4A5, 0xE252, 0x7939, 0xBC9C, 0x565E, 0x233F, 0x919F, - 0xC8CF, 0xE467, 0xF233, 0xF919, 0xFC8C, 0x7656, 0x333B, 0x999D, 0xCCCE, - 0x6E77, 0xB73B, 0xDB9D, 0xEDCE, 0x7EF7, 0xBF7B, 0xDFBD, 0xEFDE, 0x0001, - 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, - 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000 - }; - - /** - * Confirmed Packet Data Unit CRC-9 checksums, generated by: - * - * long[] table = generate( 135, 9, 0x259l, 0x1FF, false, Parity.NONE ); - */ - public static final int[] CRC9_CHECKSUMS = new int[] - { - 0x1E7, 0x1F3, 0x1F9, 0x1FC, 0x0D2, 0x045, 0x122, 0x0BD, 0x15E, 0x083, - 0x141, 0x1A0, 0x0FC, 0x052, 0x005, 0x102, 0x0AD, 0x156, 0x087, 0x143, - 0x1A1, 0x1D0, 0x0C4, 0x04E, 0x00B, 0x105, 0x182, 0x0ED, 0x176, 0x097, - 0x14B, 0x1A5, 0x1D2, 0x0C5, 0x162, 0x09D, 0x14E, 0x08B, 0x145, 0x1A2, - 0x0FD, 0x17E, 0x093, 0x149, 0x1A4, 0x0FE, 0x053, 0x129, 0x194, 0x0E6, - 0x05F, 0x12F, 0x197, 0x1CB, 0x1E5, 0x1F2, 0x0D5, 0x16A, 0x099, 0x14C, - 0x08A, 0x069, 0x134, 0x0B6, 0x077, 0x13B, 0x19D, 0x1CE, 0x0CB, 0x165, - 0x1B2, 0x0F5, 0x17A, 0x091, 0x148, 0x088, 0x068, 0x018, 0x020, 0x03C, - 0x302, 0x035, 0x11A, 0x0A1, 0x150, 0x084, 0x06E, 0x01B, 0x10D, 0x186, - 0x0EF, 0x177, 0x1BB, 0x1DD, 0x1EE, 0x0DB, 0x16D, 0x1B6, 0x0F7, 0x17B, - 0x1BD, 0x1DE, 0x0C3, 0x161, 0x1B0, 0x0F4, 0x056, 0x007, 0x103, 0x181, - 0x1C0, 0x0CC, 0x04A, 0x009, 0x104, 0x0AE, 0x07B, 0x13D, 0x19E, 0x0E3, - 0x171, 0x1B8, 0x0F0, 0x054, 0x006, 0x02F, 0x117, 0x18B, 0x1C5, 0x1E2, - 0x0DD, 0x16E, 0x09B, 0x14D, 0x1A6 - }; - - /** - * CRC-32 checksums for PDU1 ( HEADER + 1 Block ) messages, generated by: - * - * CRCUtil.generate( 64, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); - */ - public static final long[] PDU1_CHECKSUMS = new long[] - { - 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, 0x52275834l, 0x2B7322C1l, - 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, 0xD22F119Al, 0x6B770616l, - 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, 0xC663420Cl, 0x61512FDDl, - 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, 0x97BD7626l, 0x49BE35C8l, - 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, 0xE4D7F287l, 0xF26BF943l, - 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, 0xBE16F8F9l, 0xDF0B7C7Cl, - 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, 0x2EEC6FAAl, 0x1516B90El, - 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, 0x43E5D726l, 0x23926548l, - 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, 0xE275378Fl, 0xF13A9BC7l, - 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, 0x7D732767l, 0xBEB993B3l, - 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, 0xBADBDE56l, 0x5F0D61F0l, - 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, 0x67DC491Fl, 0xB3EE248Fl, - 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, 0xFB3EE248l, 0x00000001l, - 0x00000002l, 0x00000004l, 0x00000008l, 0x00000010l, 0x00000020l, - 0x00000040l, 0x00000080l, 0x00000100l, 0x00000200l, 0x00000400l, - 0x00000800l, 0x00001000l, 0x00002000l, 0x00004000l, 0x00008000l, - 0x00010000l, 0x00020000l, 0x00040000l, 0x00080000l, 0x00100000l, - 0x00200000l, 0x00400000l, 0x00800000l, 0x01000000l, 0x02000000l, - 0x04000000l, 0x08000000l, 0x10000000l, 0x20000000l, 0x40000000l, - 0x80000000l - }; - - /** - * CRC-32 checksums for PDU2 ( HEADER + 2 Blocks ) messages, generated by: - * - * CRCUtil.generate( 160, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); - */ - public static final long[] PDU2_CHECKSUMS = new long[] - { - 0x9D231959l, 0xCE918CACl, 0x6528488Dl, 0xB2942446l, 0x5B2A9CF8l, - 0x2FF5C0A7l, 0x97FAE053l, 0xCBFD7029l, 0xE5FEB814l, 0x709FD2D1l, - 0xB84FE968l, 0x5E477A6Fl, 0xAF23BD37l, 0xD791DE9Bl, 0xEBC8EF4Dl, - 0xF5E477A6l, 0x7892B508l, 0x3E29D45Fl, 0x9F14EA2Fl, 0xCF8A7517l, - 0xE7C53A8Bl, 0xF3E29D45l, 0xF9F14EA2l, 0x7E98298Al, 0x3D2C9A1El, - 0x1CF6C3D4l, 0x0C1BEF31l, 0x860DF798l, 0x41667517l, 0xA0B33A8Bl, - 0xD0599D45l, 0xE82CCEA2l, 0x7676E98Al, 0x395BFA1El, 0x1ECD73D4l, - 0x0D063731l, 0x86831B98l, 0x41210317l, 0xA090818Bl, 0xD04840C5l, - 0xE8242062l, 0x76729EEAl, 0x3959C1AEl, 0x1ECC6E0Cl, 0x0D06B9DDl, - 0x86835CEEl, 0x412120ACl, 0x22F01E8Dl, 0x91780F46l, 0x4ADC8978l, - 0x270ECA67l, 0x93876533l, 0xC9C3B299l, 0xE4E1D94Cl, 0x7010627Dl, - 0xB808313El, 0x5E649644l, 0x2D52C5F9l, 0x96A962FCl, 0x49343FA5l, - 0xA49A1FD2l, 0x502D8132l, 0x2A764E42l, 0x175BA9FAl, 0x09CD5A26l, - 0x068623C8l, 0x01239F3Fl, 0x8091CF9Fl, 0xC048E7CFl, 0xE02473E7l, - 0xF01239F3l, 0xF8091CF9l, 0xFC048E7Cl, 0x7C62C9E5l, 0xBE3164F2l, - 0x5D783CA2l, 0x2CDC908Al, 0x140EC69El, 0x0867ED94l, 0x06537811l, - 0x8329BC08l, 0x43F450DFl, 0xA1FA286Fl, 0xD0FD1437l, 0xE87E8A1Bl, - 0xF43F450Dl, 0xFA1FA286l, 0x7F6F5F98l, 0x3DD72117l, 0x9EEB908Bl, - 0xCF75C845l, 0xE7BAE422l, 0x71BDFCCAl, 0x3ABE70BEl, 0x1F3FB684l, - 0x0DFF5599l, 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, 0x52275834l, - 0x2B7322C1l, 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, 0xD22F119Al, - 0x6B770616l, 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, 0xC663420Cl, - 0x61512FDDl, 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, 0x97BD7626l, - 0x49BE35C8l, 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, 0xE4D7F287l, - 0xF26BF943l, 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, 0xBE16F8F9l, - 0xDF0B7C7Cl, 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, 0x2EEC6FAAl, - 0x1516B90El, 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, 0x43E5D726l, - 0x23926548l, 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, 0xE275378Fl, - 0xF13A9BC7l, 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, 0x7D732767l, - 0xBEB993B3l, 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, 0xBADBDE56l, - 0x5F0D61F0l, 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, 0x67DC491Fl, - 0xB3EE248Fl, 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, 0xFB3EE248l, - 0x00000001l, 0x00000002l, 0x00000004l, 0x00000008l, 0x00000010l, - 0x00000020l, 0x00000040l, 0x00000080l, 0x00000100l, 0x00000200l, - 0x00000400l, 0x00000800l, 0x00001000l, 0x00002000l, 0x00004000l, - 0x00008000l, 0x00010000l, 0x00020000l, 0x00040000l, 0x00080000l, - 0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l, 0x01000000l, - 0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l, 0x20000000l, - 0x40000000l, 0x80000000l - }; - - /** - * CRC-32 checksums for PDU3 ( HEADER + 3 Blocks ) messages, generated by: - * - * CRCUtil.generate( 256, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); - */ - public static final long[] PDU3_CHECKSUMS = new long[] - { - 0xAA5FA470l, 0x574F5CE3l, 0xABA7AE71l, 0xD5D3D738l, 0x68896547l, - 0xB444B2A3l, 0xDA225951l, 0xED112CA8l, 0x74E8188Fl, 0xBA740C47l, - 0xDD3A0623l, 0xEE9D0311l, 0xF74E8188l, 0x79C7CE1Fl, 0xBCE3E70Fl, - 0xDE71F387l, 0xEF38F9C3l, 0xF79C7CE1l, 0xFBCE3E70l, 0x7F8791E3l, - 0xBFC3C8F1l, 0xDFE1E478l, 0x6D907CE7l, 0xB6C83E73l, 0xDB641F39l, - 0xEDB20F9Cl, 0x74B98915l, 0xBA5CC48Al, 0x5F4EEC9El, 0x2DC7F894l, - 0x14837291l, 0x8A41B948l, 0x4740527Fl, 0xA3A0293Fl, 0xD1D0149Fl, - 0xE8E80A4Fl, 0xF4740527l, 0xFA3A0293l, 0xFD1D0149l, 0xFE8E80A4l, - 0x7D27CE89l, 0xBE93E744l, 0x5D297D79l, 0xAE94BEBCl, 0x552AD185l, - 0xAA9568C2l, 0x572A3ABAl, 0x29F59386l, 0x169A4718l, 0x092DAD57l, - 0x8496D6ABl, 0xC24B6B55l, 0xE125B5AAl, 0x72F2540El, 0x3B19A4DCl, - 0x1FEC5CB5l, 0x8FF62E5Al, 0x459B99F6l, 0x20AD4220l, 0x12362FCBl, - 0x891B17E5l, 0xC48D8BF2l, 0x60264B22l, 0x3273AB4Al, 0x1B595B7El, - 0x0FCC2364l, 0x05869F69l, 0x82C34FB4l, 0x43012901l, 0xA1809480l, - 0x52A0C49Bl, 0xA950624Dl, 0xD4A83126l, 0x68349648l, 0x367AC5FFl, - 0x9B3D62FFl, 0xCD9EB17Fl, 0xE6CF58BFl, 0xF367AC5Fl, 0xF9B3D62Fl, - 0xFCD9EB17l, 0xFE6CF58Bl, 0xFF367AC5l, 0xFF9B3D62l, 0x7DAD106Al, - 0x3CB606EEl, 0x1C3B8DACl, 0x0C7D480Dl, 0x863EA406l, 0x417FDCD8l, - 0x22DF60B7l, 0x916FB05Bl, 0xC8B7D82Dl, 0xE45BEC16l, 0x704D78D0l, - 0x3A4632B3l, 0x9D231959l, 0xCE918CACl, 0x6528488Dl, 0xB2942446l, - 0x5B2A9CF8l, 0x2FF5C0A7l, 0x97FAE053l, 0xCBFD7029l, 0xE5FEB814l, - 0x709FD2D1l, 0xB84FE968l, 0x5E477A6Fl, 0xAF23BD37l, 0xD791DE9Bl, - 0xEBC8EF4Dl, 0xF5E477A6l, 0x7892B508l, 0x3E29D45Fl, 0x9F14EA2Fl, - 0xCF8A7517l, 0xE7C53A8Bl, 0xF3E29D45l, 0xF9F14EA2l, 0x7E98298Al, - 0x3D2C9A1El, 0x1CF6C3D4l, 0x0C1BEF31l, 0x860DF798l, 0x41667517l, - 0xA0B33A8Bl, 0xD0599D45l, 0xE82CCEA2l, 0x7676E98Al, 0x395BFA1El, - 0x1ECD73D4l, 0x0D063731l, 0x86831B98l, 0x41210317l, 0xA090818Bl, - 0xD04840C5l, 0xE8242062l, 0x76729EEAl, 0x3959C1AEl, 0x1ECC6E0Cl, - 0x0D06B9DDl, 0x86835CEEl, 0x412120ACl, 0x22F01E8Dl, 0x91780F46l, - 0x4ADC8978l, 0x270ECA67l, 0x93876533l, 0xC9C3B299l, 0xE4E1D94Cl, - 0x7010627Dl, 0xB808313El, 0x5E649644l, 0x2D52C5F9l, 0x96A962FCl, - 0x49343FA5l, 0xA49A1FD2l, 0x502D8132l, 0x2A764E42l, 0x175BA9FAl, - 0x09CD5A26l, 0x068623C8l, 0x01239F3Fl, 0x8091CF9Fl, 0xC048E7CFl, - 0xE02473E7l, 0xF01239F3l, 0xF8091CF9l, 0xFC048E7Cl, 0x7C62C9E5l, - 0xBE3164F2l, 0x5D783CA2l, 0x2CDC908Al, 0x140EC69El, 0x0867ED94l, - 0x06537811l, 0x8329BC08l, 0x43F450DFl, 0xA1FA286Fl, 0xD0FD1437l, - 0xE87E8A1Bl, 0xF43F450Dl, 0xFA1FA286l, 0x7F6F5F98l, 0x3DD72117l, - 0x9EEB908Bl, 0xCF75C845l, 0xE7BAE422l, 0x71BDFCCAl, 0x3ABE70BEl, - 0x1F3FB684l, 0x0DFF5599l, 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, - 0x52275834l, 0x2B7322C1l, 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, - 0xD22F119Al, 0x6B770616l, 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, - 0xC663420Cl, 0x61512FDDl, 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, - 0x97BD7626l, 0x49BE35C8l, 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, - 0xE4D7F287l, 0xF26BF943l, 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, - 0xBE16F8F9l, 0xDF0B7C7Cl, 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, - 0x2EEC6FAAl, 0x1516B90El, 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, - 0x43E5D726l, 0x23926548l, 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, - 0xE275378Fl, 0xF13A9BC7l, 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, - 0x7D732767l, 0xBEB993B3l, 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, - 0xBADBDE56l, 0x5F0D61F0l, 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, - 0x67DC491Fl, 0xB3EE248Fl, 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, - 0xFB3EE248l, 0x00000001l, 0x00000002l, 0x00000004l, 0x00000008l, - 0x00000010l, 0x00000020l, 0x00000040l, 0x00000080l, 0x00000100l, - 0x00000200l, 0x00000400l, 0x00000800l, 0x00001000l, 0x00002000l, - 0x00004000l, 0x00008000l, 0x00010000l, 0x00020000l, 0x00040000l, - 0x00080000l, 0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l, - 0x01000000l, 0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l, - 0x20000000l, 0x40000000l, 0x80000000l - }; - - /** - * Performs error detection and single-bit error correction against the - * data blocks of a PDU1 message. - */ - public static BinaryMessage correctPDU1( BinaryMessage message ) - { - return correctPDU( message, PDU1_CHECKSUMS, 224 ); - } + private final static Logger mLog = LoggerFactory.getLogger(CRCP25.class); - /** - * Performs error detection and single-bit error correction against the - * data blocks of a PDU2 message. - */ - public static BinaryMessage correctPDU2( BinaryMessage message ) - { - return correctPDU( message, PDU2_CHECKSUMS, 320 ); - } + /** + * CRC-CCITT 16-bit checksums for a message length of 80 bits plus 16 + * additional checksums representing CRC checksum bit errors + * + * Generated by: + * CRCUtil.generate( 80, 16, 0x11021l, 0xFFFFl, true ); + */ + public static final int[] CCITT_80_CHECKSUMS = new int[] + { + 0x1BCB, 0x8DE5, 0xC6F2, 0x6B69, 0xB5B4, 0x52CA, 0x2175, 0x90BA, 0x404D, + 0xA026, 0x5803, 0xAC01, 0xD600, 0x6310, 0x3998, 0x14DC, 0x27E, 0x92F, + 0x8497, 0xC24B, 0xE125, 0xF092, 0x7059, 0xB82C, 0x5406, 0x2213, 0x9109, + 0xC884, 0x6C52, 0x3E39, 0x9F1C, 0x479E, 0x2BDF, 0x95EF, 0xCAF7, 0xE57B, + 0xF2BD, 0xF95E, 0x74BF, 0xBA5F, 0xDD2F, 0xEE97, 0xF74B, 0xFBA5, 0xFDD2, + 0x76F9, 0xBB7C, 0x55AE, 0x22C7, 0x9163, 0xC8B1, 0xE458, 0x7A3C, 0x350E, + 0x1297, 0x894B, 0xC4A5, 0xE252, 0x7939, 0xBC9C, 0x565E, 0x233F, 0x919F, + 0xC8CF, 0xE467, 0xF233, 0xF919, 0xFC8C, 0x7656, 0x333B, 0x999D, 0xCCCE, + 0x6E77, 0xB73B, 0xDB9D, 0xEDCE, 0x7EF7, 0xBF7B, 0xDFBD, 0xEFDE, 0x0001, + 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, + 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000 + }; - /** - * Performs error detection and single-bit error correction against the - * data blocks of a PDU3 message. - */ - public static BinaryMessage correctPDU3( BinaryMessage message ) - { - return correctPDU( message, PDU3_CHECKSUMS, 416 ); - } + /** + * Confirmed Packet Data Unit CRC-9 checksums, generated by: + * + * long[] table = CRCUtil.generate( 135, 9, 0x259l, 0x1FF, false, Parity.NONE ); + */ + public static final int[] CRC9_CHECKSUMS = new int[] + { + 0x1E7, 0x1F3, 0x1F9, 0x1FC, 0x0D2, 0x045, 0x122, 0x0BD, 0x15E, 0x083, + 0x141, 0x1A0, 0x0FC, 0x052, 0x005, 0x102, 0x0AD, 0x156, 0x087, 0x143, + 0x1A1, 0x1D0, 0x0C4, 0x04E, 0x00B, 0x105, 0x182, 0x0ED, 0x176, 0x097, + 0x14B, 0x1A5, 0x1D2, 0x0C5, 0x162, 0x09D, 0x14E, 0x08B, 0x145, 0x1A2, + 0x0FD, 0x17E, 0x093, 0x149, 0x1A4, 0x0FE, 0x053, 0x129, 0x194, 0x0E6, + 0x05F, 0x12F, 0x197, 0x1CB, 0x1E5, 0x1F2, 0x0D5, 0x16A, 0x099, 0x14C, + 0x08A, 0x069, 0x134, 0x0B6, 0x077, 0x13B, 0x19D, 0x1CE, 0x0CB, 0x165, + 0x1B2, 0x0F5, 0x17A, 0x091, 0x148, 0x088, 0x068, 0x018, 0x020, 0x03C, + 0x032, 0x035, 0x11A, 0x0A1, 0x150, 0x084, 0x06E, 0x01B, 0x10D, 0x186, + 0x0EF, 0x177, 0x1BB, 0x1DD, 0x1EE, 0x0DB, 0x16D, 0x1B6, 0x0F7, 0x17B, + 0x1BD, 0x1DE, 0x0C3, 0x161, 0x1B0, 0x0F4, 0x056, 0x007, 0x103, 0x181, + 0x1C0, 0x0CC, 0x04A, 0x009, 0x104, 0x0AE, 0x07B, 0x13D, 0x19E, 0x0E3, + 0x171, 0x1B8, 0x0F0, 0x054, 0x006, 0x02F, 0x117, 0x18B, 0x1C5, 0x1E2, + 0x0DD, 0x16E, 0x09B, 0x14D, 0x1A6 + }; - public static BinaryMessage correctPDU( BinaryMessage message, long[] checksums, int crcStart ) - { - long calculated = 0; //Starting value - - int messageStart = 160; + /** + * CRC-32 checksums for PDU1 ( HEADER + 1 Block ) messages, generated by: + * + * CRCUtil.generate( 64, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); + */ + public static final long[] PDU1_CHECKSUMS = new long[] + { + 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, 0x52275834l, 0x2B7322C1l, + 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, 0xD22F119Al, 0x6B770616l, + 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, 0xC663420Cl, 0x61512FDDl, + 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, 0x97BD7626l, 0x49BE35C8l, + 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, 0xE4D7F287l, 0xF26BF943l, + 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, 0xBE16F8F9l, 0xDF0B7C7Cl, + 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, 0x2EEC6FAAl, 0x1516B90El, + 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, 0x43E5D726l, 0x23926548l, + 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, 0xE275378Fl, 0xF13A9BC7l, + 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, 0x7D732767l, 0xBEB993B3l, + 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, 0xBADBDE56l, 0x5F0D61F0l, + 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, 0x67DC491Fl, 0xB3EE248Fl, + 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, 0xFB3EE248l, 0x00000001l, + 0x00000002l, 0x00000004l, 0x00000008l, 0x00000010l, 0x00000020l, + 0x00000040l, 0x00000080l, 0x00000100l, 0x00000200l, 0x00000400l, + 0x00000800l, 0x00001000l, 0x00002000l, 0x00004000l, 0x00008000l, + 0x00010000l, 0x00020000l, 0x00040000l, 0x00080000l, 0x00100000l, + 0x00200000l, 0x00400000l, 0x00800000l, 0x01000000l, 0x02000000l, + 0x04000000l, 0x08000000l, 0x10000000l, 0x20000000l, 0x40000000l, + 0x80000000l + }; - /* Iterate the set bits and XOR running checksum with lookup value */ - for (int i = message.nextSetBit( messageStart ); - i >= messageStart && i < crcStart; - i = message.nextSetBit( i+1 ) ) - { - calculated ^= checksums[ i - messageStart ]; - } - - long checksum = getLongChecksum( message, crcStart, 32 ); + /** + * CRC-32 checksums for PDU2 ( HEADER + 2 Blocks ) messages, generated by: + * + * CRCUtil.generate( 160, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); + */ + public static final long[] PDU2_CHECKSUMS = new long[] + { + 0x9D231959l, 0xCE918CACl, 0x6528488Dl, 0xB2942446l, 0x5B2A9CF8l, + 0x2FF5C0A7l, 0x97FAE053l, 0xCBFD7029l, 0xE5FEB814l, 0x709FD2D1l, + 0xB84FE968l, 0x5E477A6Fl, 0xAF23BD37l, 0xD791DE9Bl, 0xEBC8EF4Dl, + 0xF5E477A6l, 0x7892B508l, 0x3E29D45Fl, 0x9F14EA2Fl, 0xCF8A7517l, + 0xE7C53A8Bl, 0xF3E29D45l, 0xF9F14EA2l, 0x7E98298Al, 0x3D2C9A1El, + 0x1CF6C3D4l, 0x0C1BEF31l, 0x860DF798l, 0x41667517l, 0xA0B33A8Bl, + 0xD0599D45l, 0xE82CCEA2l, 0x7676E98Al, 0x395BFA1El, 0x1ECD73D4l, + 0x0D063731l, 0x86831B98l, 0x41210317l, 0xA090818Bl, 0xD04840C5l, + 0xE8242062l, 0x76729EEAl, 0x3959C1AEl, 0x1ECC6E0Cl, 0x0D06B9DDl, + 0x86835CEEl, 0x412120ACl, 0x22F01E8Dl, 0x91780F46l, 0x4ADC8978l, + 0x270ECA67l, 0x93876533l, 0xC9C3B299l, 0xE4E1D94Cl, 0x7010627Dl, + 0xB808313El, 0x5E649644l, 0x2D52C5F9l, 0x96A962FCl, 0x49343FA5l, + 0xA49A1FD2l, 0x502D8132l, 0x2A764E42l, 0x175BA9FAl, 0x09CD5A26l, + 0x068623C8l, 0x01239F3Fl, 0x8091CF9Fl, 0xC048E7CFl, 0xE02473E7l, + 0xF01239F3l, 0xF8091CF9l, 0xFC048E7Cl, 0x7C62C9E5l, 0xBE3164F2l, + 0x5D783CA2l, 0x2CDC908Al, 0x140EC69El, 0x0867ED94l, 0x06537811l, + 0x8329BC08l, 0x43F450DFl, 0xA1FA286Fl, 0xD0FD1437l, 0xE87E8A1Bl, + 0xF43F450Dl, 0xFA1FA286l, 0x7F6F5F98l, 0x3DD72117l, 0x9EEB908Bl, + 0xCF75C845l, 0xE7BAE422l, 0x71BDFCCAl, 0x3ABE70BEl, 0x1F3FB684l, + 0x0DFF5599l, 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, 0x52275834l, + 0x2B7322C1l, 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, 0xD22F119Al, + 0x6B770616l, 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, 0xC663420Cl, + 0x61512FDDl, 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, 0x97BD7626l, + 0x49BE35C8l, 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, 0xE4D7F287l, + 0xF26BF943l, 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, 0xBE16F8F9l, + 0xDF0B7C7Cl, 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, 0x2EEC6FAAl, + 0x1516B90El, 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, 0x43E5D726l, + 0x23926548l, 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, 0xE275378Fl, + 0xF13A9BC7l, 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, 0x7D732767l, + 0xBEB993B3l, 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, 0xBADBDE56l, + 0x5F0D61F0l, 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, 0x67DC491Fl, + 0xB3EE248Fl, 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, 0xFB3EE248l, + 0x00000001l, 0x00000002l, 0x00000004l, 0x00000008l, 0x00000010l, + 0x00000020l, 0x00000040l, 0x00000080l, 0x00000100l, 0x00000200l, + 0x00000400l, 0x00000800l, 0x00001000l, 0x00002000l, 0x00004000l, + 0x00008000l, 0x00010000l, 0x00020000l, 0x00040000l, 0x00080000l, + 0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l, 0x01000000l, + 0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l, 0x20000000l, + 0x40000000l, 0x80000000l + }; - long error = calculated ^ checksum; - - if( error == 0 || error == 0xFFFFFFFFl ) - { - message.setCRC( CRC.PASSED ); - - return message; - } - else - { - int errorLocation = getBitError( error, checksums ); - - if( errorLocation >= 0 ) - { - message.flip( errorLocation + messageStart ); + /** + * CRC-32 checksums for PDU3 ( HEADER + 3 Blocks ) messages, generated by: + * + * CRCUtil.generate( 256, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); + */ + public static final long[] PDU3_CHECKSUMS = new long[] + { + 0xAA5FA470l, 0x574F5CE3l, 0xABA7AE71l, 0xD5D3D738l, 0x68896547l, + 0xB444B2A3l, 0xDA225951l, 0xED112CA8l, 0x74E8188Fl, 0xBA740C47l, + 0xDD3A0623l, 0xEE9D0311l, 0xF74E8188l, 0x79C7CE1Fl, 0xBCE3E70Fl, + 0xDE71F387l, 0xEF38F9C3l, 0xF79C7CE1l, 0xFBCE3E70l, 0x7F8791E3l, + 0xBFC3C8F1l, 0xDFE1E478l, 0x6D907CE7l, 0xB6C83E73l, 0xDB641F39l, + 0xEDB20F9Cl, 0x74B98915l, 0xBA5CC48Al, 0x5F4EEC9El, 0x2DC7F894l, + 0x14837291l, 0x8A41B948l, 0x4740527Fl, 0xA3A0293Fl, 0xD1D0149Fl, + 0xE8E80A4Fl, 0xF4740527l, 0xFA3A0293l, 0xFD1D0149l, 0xFE8E80A4l, + 0x7D27CE89l, 0xBE93E744l, 0x5D297D79l, 0xAE94BEBCl, 0x552AD185l, + 0xAA9568C2l, 0x572A3ABAl, 0x29F59386l, 0x169A4718l, 0x092DAD57l, + 0x8496D6ABl, 0xC24B6B55l, 0xE125B5AAl, 0x72F2540El, 0x3B19A4DCl, + 0x1FEC5CB5l, 0x8FF62E5Al, 0x459B99F6l, 0x20AD4220l, 0x12362FCBl, + 0x891B17E5l, 0xC48D8BF2l, 0x60264B22l, 0x3273AB4Al, 0x1B595B7El, + 0x0FCC2364l, 0x05869F69l, 0x82C34FB4l, 0x43012901l, 0xA1809480l, + 0x52A0C49Bl, 0xA950624Dl, 0xD4A83126l, 0x68349648l, 0x367AC5FFl, + 0x9B3D62FFl, 0xCD9EB17Fl, 0xE6CF58BFl, 0xF367AC5Fl, 0xF9B3D62Fl, + 0xFCD9EB17l, 0xFE6CF58Bl, 0xFF367AC5l, 0xFF9B3D62l, 0x7DAD106Al, + 0x3CB606EEl, 0x1C3B8DACl, 0x0C7D480Dl, 0x863EA406l, 0x417FDCD8l, + 0x22DF60B7l, 0x916FB05Bl, 0xC8B7D82Dl, 0xE45BEC16l, 0x704D78D0l, + 0x3A4632B3l, 0x9D231959l, 0xCE918CACl, 0x6528488Dl, 0xB2942446l, + 0x5B2A9CF8l, 0x2FF5C0A7l, 0x97FAE053l, 0xCBFD7029l, 0xE5FEB814l, + 0x709FD2D1l, 0xB84FE968l, 0x5E477A6Fl, 0xAF23BD37l, 0xD791DE9Bl, + 0xEBC8EF4Dl, 0xF5E477A6l, 0x7892B508l, 0x3E29D45Fl, 0x9F14EA2Fl, + 0xCF8A7517l, 0xE7C53A8Bl, 0xF3E29D45l, 0xF9F14EA2l, 0x7E98298Al, + 0x3D2C9A1El, 0x1CF6C3D4l, 0x0C1BEF31l, 0x860DF798l, 0x41667517l, + 0xA0B33A8Bl, 0xD0599D45l, 0xE82CCEA2l, 0x7676E98Al, 0x395BFA1El, + 0x1ECD73D4l, 0x0D063731l, 0x86831B98l, 0x41210317l, 0xA090818Bl, + 0xD04840C5l, 0xE8242062l, 0x76729EEAl, 0x3959C1AEl, 0x1ECC6E0Cl, + 0x0D06B9DDl, 0x86835CEEl, 0x412120ACl, 0x22F01E8Dl, 0x91780F46l, + 0x4ADC8978l, 0x270ECA67l, 0x93876533l, 0xC9C3B299l, 0xE4E1D94Cl, + 0x7010627Dl, 0xB808313El, 0x5E649644l, 0x2D52C5F9l, 0x96A962FCl, + 0x49343FA5l, 0xA49A1FD2l, 0x502D8132l, 0x2A764E42l, 0x175BA9FAl, + 0x09CD5A26l, 0x068623C8l, 0x01239F3Fl, 0x8091CF9Fl, 0xC048E7CFl, + 0xE02473E7l, 0xF01239F3l, 0xF8091CF9l, 0xFC048E7Cl, 0x7C62C9E5l, + 0xBE3164F2l, 0x5D783CA2l, 0x2CDC908Al, 0x140EC69El, 0x0867ED94l, + 0x06537811l, 0x8329BC08l, 0x43F450DFl, 0xA1FA286Fl, 0xD0FD1437l, + 0xE87E8A1Bl, 0xF43F450Dl, 0xFA1FA286l, 0x7F6F5F98l, 0x3DD72117l, + 0x9EEB908Bl, 0xCF75C845l, 0xE7BAE422l, 0x71BDFCCAl, 0x3ABE70BEl, + 0x1F3FB684l, 0x0DFF5599l, 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, + 0x52275834l, 0x2B7322C1l, 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, + 0xD22F119Al, 0x6B770616l, 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, + 0xC663420Cl, 0x61512FDDl, 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, + 0x97BD7626l, 0x49BE35C8l, 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, + 0xE4D7F287l, 0xF26BF943l, 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, + 0xBE16F8F9l, 0xDF0B7C7Cl, 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, + 0x2EEC6FAAl, 0x1516B90El, 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, + 0x43E5D726l, 0x23926548l, 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, + 0xE275378Fl, 0xF13A9BC7l, 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, + 0x7D732767l, 0xBEB993B3l, 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, + 0xBADBDE56l, 0x5F0D61F0l, 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, + 0x67DC491Fl, 0xB3EE248Fl, 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, + 0xFB3EE248l, 0x00000001l, 0x00000002l, 0x00000004l, 0x00000008l, + 0x00000010l, 0x00000020l, 0x00000040l, 0x00000080l, 0x00000100l, + 0x00000200l, 0x00000400l, 0x00000800l, 0x00001000l, 0x00002000l, + 0x00004000l, 0x00008000l, 0x00010000l, 0x00020000l, 0x00040000l, + 0x00080000l, 0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l, + 0x01000000l, 0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l, + 0x20000000l, 0x40000000l, 0x80000000l + }; - message.setCRC( CRC.CORRECTED ); - - return message; - } - } + /** + * Performs error detection and single-bit error correction against the + * data blocks of a PDU1 message. + */ + public static BinaryMessage correctPDU1(BinaryMessage message) + { + return correctPDU(message, PDU1_CHECKSUMS, 224); + } - message.setCRC( CRC.FAILED_CRC ); - - return message; - } + /** + * Performs error detection and single-bit error correction against the + * data blocks of a PDU2 message. + */ + public static BinaryMessage correctPDU2(BinaryMessage message) + { + return correctPDU(message, PDU2_CHECKSUMS, 320); + } - /** - * Error detection and correction of single-bit errors for CCITT 16-bit - * CRC protected 80-bit messages. - */ - public static BinaryMessage correctCCITT80( BinaryMessage message, - int messageStart, - int crcStart ) - { - int calculated = 0; //Starting value + /** + * Performs error detection and single-bit error correction against the + * data blocks of a PDU3 message. + */ + public static BinaryMessage correctPDU3(BinaryMessage message) + { + return correctPDU(message, PDU3_CHECKSUMS, 416); + } - /* Iterate the set bits and XOR running checksum with lookup value */ - for (int i = message.nextSetBit( messageStart ); - i >= messageStart && i < crcStart; - i = message.nextSetBit( i+1 ) ) - { - calculated ^= CCITT_80_CHECKSUMS[ i - messageStart ]; - } - - int checksum = getIntChecksum( message, crcStart, 16 ); + public static BinaryMessage correctPDU(BinaryMessage message, long[] checksums, int crcStart) + { + long calculated = 0; //Starting value - int residual = calculated ^ checksum; - - if( residual == 0 || residual == 0xFFFF ) - { - message.setCRC( CRC.PASSED ); - - return message; - } - else - { - int errorLocation = getBitError( residual, CCITT_80_CHECKSUMS ); - - if( errorLocation >= 0 ) - { - message.flip( errorLocation + messageStart ); - - message.setCRC( CRC.CORRECTED ); - - return message; - } - } + int messageStart = 160; - message.setCRC( CRC.FAILED_CRC ); - - return message; - } + /* Iterate the set bits and XOR running checksum with lookup value */ + for(int i = message.nextSetBit(messageStart); + i >= messageStart && i < crcStart; + i = message.nextSetBit(i + 1)) + { + calculated ^= checksums[i - messageStart]; + } - /** - * Error detection for CRC-9 protected Confirmed Packet Data blocks. These - * data blocks have a slightly complicated structure because the checksum - * is located between bits 7-15, within a 144 bit block. The checksums - * were generated assuming that the message is contiguous from 0 - 134 bits. - * No data correction is performed. - */ - public static CRC checkCRC9( BinaryMessage message, int messageStart ) - { - int calculated = 0x0; //Initial fill of all ones + long checksum = getLongChecksum(message, crcStart, 32); - /* Iterate the set bits and XOR running checksum with lookup value */ - for (int i = message.nextSetBit( messageStart ); - i >= messageStart && i < messageStart + 144; - i = message.nextSetBit( i+1 ) ) - { - /* message bits before the CRC */ - if( i < ( messageStart + 7 ) ) - { - calculated ^= CRC9_CHECKSUMS[ i - messageStart ]; - } - /* message bits after the CRC */ - else if( i > ( messageStart + 15 ) ) - { - calculated ^= CRC9_CHECKSUMS[ i - messageStart - 9 ]; - } - } - - int checksum = message.getInt( messageStart + 7, messageStart + 15 ); + long error = calculated ^ checksum; + + if(error == 0 || error == 0xFFFFFFFFl) + { + message.setCRC(CRC.PASSED); + + return message; + } + else + { + int errorLocation = getBitError(error, checksums); + + if(errorLocation >= 0) + { + message.flip(errorLocation + messageStart); + + message.setCRC(CRC.CORRECTED); + + return message; + } + } + + message.setCRC(CRC.FAILED_CRC); + + return message; + } + + /** + * Error detection and correction of single-bit errors for CCITT 16-bit + * CRC protected 80-bit messages. + */ + public static BinaryMessage correctCCITT80(BinaryMessage message, + int messageStart, + int crcStart) + { + int calculated = 0; //Starting value + + /* Iterate the set bits and XOR running checksum with lookup value */ + for(int i = message.nextSetBit(messageStart); + i >= messageStart && i < crcStart; + i = message.nextSetBit(i + 1)) + { + calculated ^= CCITT_80_CHECKSUMS[i - messageStart]; + } + + int checksum = getIntChecksum(message, crcStart, 16); + + int residual = calculated ^ checksum; + + if(residual == 0 || residual == 0xFFFF) + { + message.setCRC(CRC.PASSED); + + return message; + } + else + { + int errorLocation = getBitError(residual, CCITT_80_CHECKSUMS); + + if(errorLocation >= 0) + { + message.flip(errorLocation + messageStart); + + message.setCRC(CRC.CORRECTED); + + return message; + } + } + + message.setCRC(CRC.FAILED_CRC); + + return message; + } + + /** + * Error detection and correction of single-bit errors for CCITT 16-bit + * CRC protected 80-bit messages. + */ + public static int correctCCITT80(CorrectedBinaryMessage message, int messageStart, int crcStart) + { + int calculated = 0xFFFF; //Starting value + + /* Iterate the set bits and XOR running checksum with lookup value */ + for(int i = message.nextSetBit(messageStart); i >= messageStart && i < crcStart; i = message.nextSetBit(i + 1)) + { + calculated ^= CCITT_80_CHECKSUMS[i - messageStart]; + } + + int checksum = getIntChecksum(message, crcStart, 16); + + int residual = calculated ^ checksum; + + if(residual == 0 || residual == 0xFFFF) + { + return 0; + } + else + { + int errorLocation = getBitError(residual, CCITT_80_CHECKSUMS); + + if(errorLocation >= 0) + { + message.flip(errorLocation + messageStart); + message.incrementCorrectedBitCount(1); + return 1; + } + } + + //Message has at least 2 bit errors - ie uncorrectable + message.incrementCorrectedBitCount(2); + + return 2; + } + + /** + * Error detection for CRC-9 protected Confirmed Packet Data blocks. These + * data blocks have a slightly complicated structure because the checksum + * is located between bits 7-15, within a 144 bit block. The checksums + * were generated assuming that the message is contiguous from 0 - 134 bits. + * No data correction is performed. + */ + public static CRC checkCRC9(BinaryMessage message, int messageStart) + { + int calculated = 0x0; //Initial fill of all ones + + /* Iterate the set bits and XOR running checksum with lookup value */ + for(int i = message.nextSetBit(messageStart); + i >= messageStart && i < messageStart + 144; + i = message.nextSetBit(i + 1)) + { + /* message bits before the CRC */ + if(i < (messageStart + 7)) + { + calculated ^= CRC9_CHECKSUMS[i - messageStart]; + } + /* message bits after the CRC */ + else if(i > (messageStart + 15)) + { + calculated ^= CRC9_CHECKSUMS[i - messageStart - 9]; + } + } + + int checksum = message.getInt(messageStart + 7, messageStart + 15); + + int residual = calculated ^ checksum; - int residual = calculated ^ checksum; - // mLog.debug( "CALC:" + calculated + " CHECK:" + checksum + " RESID:" + residual ); - if( residual == 0 || residual == 0x1FF ) - { - return CRC.PASSED; - } - - return CRC.FAILED_CRC; - } - - - /** - * Performs Galois 24/12/7 error detection and correction against the 12 - * encoded 24-bit message segments following the 64-bit NID in the message - * - * @return - true if all 12 segments of the message can be checked/corrected - */ - public static boolean correctGalois24( BinaryMessage tdulc ) - { - boolean passes = true; - - int x = 64; - - while( x < tdulc.size() && passes ) - { - - tdulc = Golay24.checkAndCorrect( tdulc, x ); - - passes = tdulc.getCRC() == CRC.PASSED; + if(residual == 0 || residual == 0x1FF) + { + return CRC.PASSED; + } - x += 24; - } - - return passes; - } - - - /** - * Calculates the value of the message checksum as a long - */ - public static long getLongChecksum( BinaryMessage message, - int crcStart, int crcLength ) - { - return message.getLong( crcStart, crcStart + crcLength - 1 ); + return CRC.FAILED_CRC; } - /** - * Calculates the value of the message checksum as an integer - */ - public static int getIntChecksum( BinaryMessage message, - int crcStart, int crcLength ) + + /** + * Performs Galois 24/12/7 error detection and correction against the 12 + * encoded 24-bit message segments following the 64-bit NID in the message + * + * @return - true if all 12 segments of the message can be checked/corrected + */ + public static boolean correctGalois24(CorrectedBinaryMessage tdulc) { - return message.getInt( crcStart, crcStart + crcLength - 1 ); + boolean passes = true; + + int x = 64; + + while(x < tdulc.size() && passes) + { + + int errors = Golay24.checkAndCorrect(tdulc, x); + + passes = errors < 2; + + x += 24; + } + + return passes; + } + + + /** + * Calculates the value of the message checksum as a long + */ + public static long getLongChecksum(BinaryMessage message, + int crcStart, int crcLength) + { + return message.getLong(crcStart, crcStart + crcLength - 1); + } + + /** + * Calculates the value of the message checksum as an integer + */ + public static int getIntChecksum(BinaryMessage message, + int crcStart, int crcLength) + { + return message.getInt(crcStart, crcStart + crcLength - 1); } /** * Identifies any single bit error position that matches the checksum error. */ - public static int getBitError( long checksumError, long[] checksums ) + public static int getBitError(long checksumError, long[] checksums) { - for( int x = 0; x < checksums.length; x++ ) - { - if( checksums[ x ] == checksumError ) - { - return x; - } - } + for(int x = 0; x < checksums.length; x++) + { + if(checksums[x] == checksumError) + { + return x; + } + } - return -1; + return -1; } - + /** * Identifies any single bit error position that matches the checksum error. */ - public static int getBitError( int checksumError, int[] checksums ) + public static int getBitError(int checksumError, int[] checksums) { - for( int x = 0; x < checksums.length; x++ ) - { - if( checksums[ x ] == checksumError ) - { - return x; - } - } + for(int x = 0; x < checksums.length; x++) + { + if(checksums[x] == checksumError) + { + return x; + } + } - return -1; + return -1; } - - public static void main( String[] args ) + + public static void main(String[] args) { - String raw = "000000001000001100000001010001111011000100001010010001111100000000000101000000000000000001000000000000110000000000000001101010101010101010101010"; - - BinaryMessage message = BinaryMessage.load( raw ); - - mLog.debug( "MSG:" + message.toString() ); + String raw = "000000001000001100000001010001111011000100001010010001111100000000000101000000000000000001000000000000110000000000000001101010101010101010101010"; - CRC results = checkCRC9( message, 0 ); + BinaryMessage message = BinaryMessage.load(raw); - mLog.debug( "COR:" + message.toString() ); + mLog.debug("MSG:" + message.toString()); - mLog.debug( "Results: " + results.getDisplayText() ); + CRC results = checkCRC9(message, 0); + + mLog.debug("COR:" + message.toString()); + + mLog.debug("Results: " + results.getDisplayText()); } } diff --git a/src/main/java/io/github/dsheirer/edac/CRCPassport.java b/src/main/java/io/github/dsheirer/edac/CRCPassport.java index 5591cb31..4ebb56da 100644 --- a/src/main/java/io/github/dsheirer/edac/CRCPassport.java +++ b/src/main/java/io/github/dsheirer/edac/CRCPassport.java @@ -1,215 +1,224 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.edac; import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import java.util.BitSet; /** * Passport CRC checksum utility - * + * * Passport message blocks are 68 bits in length as follows: - * 0 - 8: Sync - * 9 - 59: message bits - * 60 - 67: CRC-7 check bits plus 1 parity - * + * 0 - 8: Sync + * 9 - 59: message bits + * 60 - 67: CRC-7 check bits plus 1 parity + * * Passport uses a CRC-7 (0x44) with an initial fill of 0x44 plus parity set. - * + * * CRC-7 Generating Polynomial: x6 + x2 + 1 (0x44) */ public class CRCPassport { - private static final byte sFILL_00 = (byte)0x00; - - private static byte[] sCHECKSUMS = new byte[] - { - (byte)0x6E, //DCC 1 - (byte)0xBF, //DCC 0 - (byte)0xD6, //LCN 10 - (byte)0xE3, //LCN 9 - (byte)0xF8, //LCN 8 - (byte)0x7C, //LCN 7 - (byte)0x3E, //LCN 6 - (byte)0x97, //LCN 5 - (byte)0xC2, //LCN 4 - (byte)0xE9, //LCN 3 - (byte)0x75, //LCN 2 - (byte)0x3B, //LCN 1 - (byte)0x94, //LCN 0 - (byte)0x4A, //SITE 6 - (byte)0xAD, //SITE 5 - (byte)0x57, //SITE 4 - (byte)0xA2, //SITE 3 - (byte)0xD9, //SITE 2 - (byte)0x6D, //SITE 1 - (byte)0x37, //SITE 0 - (byte)0x92, //GROUP 15 - (byte)0xC1, //GROUP 14 - (byte)0x61, //GROUP 13 - (byte)0x31, //GROUP 12 - (byte)0x19, //GROUP 11 - (byte)0x0D, //GROUP 10 - (byte)0x07, //GROUP 9 - (byte)0x8A, //GROUP 8 - (byte)0xCD, //GROUP 7 - (byte)0x67, //GROUP 6 - (byte)0xBA, //GROUP 5 - (byte)0xD5, //GROUP 4 - (byte)0x6B, //GROUP 3 - (byte)0xBC, //GROUP 2 - (byte)0x5E, //GROUP 1 - (byte)0xA7, //GROUP 0 - (byte)0xDA, //TYPE 3 - (byte)0xE5, //TYPE 2 - (byte)0x73, //TYPE 1 - (byte)0xB0, //TYPE 0 - (byte)0x58, //FREE 10 - (byte)0x2C, //FREE 9 - (byte)0x16, //FREE 8 - (byte)0x83, //FREE 7 - (byte)0xC8, //FREE 6 - (byte)0x64, //FREE 5 - (byte)0x32, //FREE 4 - (byte)0x91, //FREE 3 - (byte)0x49, //FREE 2 - (byte)0x25, //FREE 1 - (byte)0x13 //FREE 0 - }; + private static final byte sFILL_00 = (byte)0x00; - /** - * Determines if message bits 9 - 59 pass the CRC checksum - * contained in bits 60 - 68, using a lookup table of CRC checksum values - * derived from the CRC-7 value and the final parity bit - */ - public static CRC check( BitSet msg ) - { - CRC crc = CRC.UNKNOWN; - - byte calculated = 0x0; //Starting value for an OSW + private static byte[] sCHECKSUMS = new byte[] + { + (byte)0x6E, //DCC 1 + (byte)0xBF, //DCC 0 + (byte)0xD6, //LCN 10 + (byte)0xE3, //LCN 9 + (byte)0xF8, //LCN 8 + (byte)0x7C, //LCN 7 + (byte)0x3E, //LCN 6 + (byte)0x97, //LCN 5 + (byte)0xC2, //LCN 4 + (byte)0xE9, //LCN 3 + (byte)0x75, //LCN 2 + (byte)0x3B, //LCN 1 + (byte)0x94, //LCN 0 + (byte)0x4A, //SITE 6 + (byte)0xAD, //SITE 5 + (byte)0x57, //SITE 4 + (byte)0xA2, //SITE 3 + (byte)0xD9, //SITE 2 + (byte)0x6D, //SITE 1 + (byte)0x37, //SITE 0 + (byte)0x92, //GROUP 15 + (byte)0xC1, //GROUP 14 + (byte)0x61, //GROUP 13 + (byte)0x31, //GROUP 12 + (byte)0x19, //GROUP 11 + (byte)0x0D, //GROUP 10 + (byte)0x07, //GROUP 9 + (byte)0x8A, //GROUP 8 + (byte)0xCD, //GROUP 7 + (byte)0x67, //GROUP 6 + (byte)0xBA, //GROUP 5 + (byte)0xD5, //GROUP 4 + (byte)0x6B, //GROUP 3 + (byte)0xBC, //GROUP 2 + (byte)0x5E, //GROUP 1 + (byte)0xA7, //GROUP 0 + (byte)0xDA, //TYPE 3 + (byte)0xE5, //TYPE 2 + (byte)0x73, //TYPE 1 + (byte)0xB0, //TYPE 0 + (byte)0x58, //FREE 10 + (byte)0x2C, //FREE 9 + (byte)0x16, //FREE 8 + (byte)0x83, //FREE 7 + (byte)0xC8, //FREE 6 + (byte)0x64, //FREE 5 + (byte)0x32, //FREE 4 + (byte)0x91, //FREE 3 + (byte)0x49, //FREE 2 + (byte)0x25, //FREE 1 + (byte)0x13 //FREE 0 + }; + + /** + * Determines if message bits 9 - 59 pass the CRC checksum + * contained in bits 60 - 68, using a lookup table of CRC checksum values + * derived from the CRC-7 value and the final parity bit + */ + public static CRC check(BitSet msg) + { + CRC crc = CRC.UNKNOWN; + + byte calculated = 0x0; //Starting value for an OSW //Iterate bits that are set and XOR running checksum with lookup value - for (int i = msg.nextSetBit( 9 ); i >= 9 && i <= 59; i = msg.nextSetBit( i+1 ) ) - { - calculated ^= sCHECKSUMS[ i - 9 ]; - } + for(int i = msg.nextSetBit(9); i >= 9 && i <= 59; i = msg.nextSetBit(i + 1)) + { + calculated ^= sCHECKSUMS[i - 9]; + } - //Apply the message checksum to derive the residual - calculated ^= getChecksum( msg ); - - switch( (byte)calculated ) - { - case sFILL_00: - crc = CRC.PASSED; - break; - default: - crc = CRC.FAILED_CRC; - } - - return crc; - } - - public static byte getResidual( BitSet msg ) + //Apply the message checksum to derive the residual + calculated ^= getChecksum(msg); + + switch((byte)calculated) + { + case sFILL_00: + crc = CRC.PASSED; + break; + default: + crc = CRC.FAILED_CRC; + } + + return crc; + } + + public static byte getResidual(BitSet msg) { byte calculated = 0x0; //Initial fill of zero //Iterate bits that are set and XOR running checksum with lookup value - for (int i = msg.nextSetBit( 9 ); i >= 9 && i <= 59; i = msg.nextSetBit( i+1 ) ) + for(int i = msg.nextSetBit(9); i >= 9 && i <= 59; i = msg.nextSetBit(i + 1)) { - calculated ^= sCHECKSUMS[ i - 9 ]; + calculated ^= sCHECKSUMS[i - 9]; } - - calculated ^= getChecksum( msg ); - + + calculated ^= getChecksum(msg); + return calculated; } - - public static byte[] getChecks() - { - return sCHECKSUMS; - } - /** - * Returns the integer value of the 7 bit crc checksum - */ - public static byte getChecksum( BitSet msg ) + public static byte[] getChecks() + { + return sCHECKSUMS; + } + + /** + * Returns the integer value of the 7 bit crc checksum + */ + public static byte getChecksum(BitSet msg) { byte retVal = 0x0; - - for( int x = 0; x < 8; x++ ) - { - if( msg.get( x + 60 ) ) - { - retVal ^= (byte)( 1<<( 7 - x ) ); - } - } - - return retVal; + + for(int x = 0; x < 8; x++) + { + if(msg.get(x + 60)) + { + retVal ^= (byte)(1 << (7 - x)); + } + } + + return retVal; } /** * Initial take on error correction. The majority of the errors detected * are in the final 2 bits, owing to the forward looking soft bit detection - * used in the decoder. For right now, correct any messages where the + * used in the decoder. For right now, correct any messages where the * residual indicates that the final 1 or 2 bits are bad. */ - public static BinaryMessage correct( BinaryMessage msg ) + public static CorrectedBinaryMessage correct(CorrectedBinaryMessage msg) { - int residual = (int)( 0xFF & getResidual( msg ) ); - - switch( residual ) - { - case 1: - case 136: - msg.flip( 67 ); - break; - case 3: - case 138: - msg.flip( 66 ); - msg.flip( 67 ); - break; - case 2: - case 139: - msg.flip( 66 ); - break; - case 140: - msg.flip( 65 ); - msg.flip( 67 ); - break; - case 153: - msg.flip( 67 ); - break; - case 142: - msg.flip( 65 ); - msg.flip( 66 ); - msg.flip( 67 ); - break; - } + int residual = (int)(0xFF & getResidual(msg)); - return msg; + switch(residual) + { + case 1: + case 136: + msg.flip(67); + msg.incrementCorrectedBitCount(1); + break; + case 3: + case 138: + msg.flip(66); + msg.flip(67); + msg.incrementCorrectedBitCount(2); + break; + case 2: + case 139: + msg.flip(66); + msg.incrementCorrectedBitCount(1); + break; + case 140: + msg.flip(65); + msg.flip(67); + msg.incrementCorrectedBitCount(2); + break; + case 153: + msg.flip(67); + msg.incrementCorrectedBitCount(1); + break; + case 142: + msg.flip(65); + msg.flip(66); + msg.flip(67); + msg.incrementCorrectedBitCount(3); + break; + } + + return msg; } - + /** * Performs an even parity check on the bitset. If the number of bits that * are set is an even number, then it passes the even parity check */ - public static boolean isEvenParity( BinaryMessage bits ) + public static boolean isEvenParity(BinaryMessage bits) { - return ( bits.cardinality() % 2 == 0 ); + return (bits.cardinality() % 2 == 0); } } diff --git a/src/main/java/io/github/dsheirer/edac/CRCUtil.java b/src/main/java/io/github/dsheirer/edac/CRCUtil.java index be5b31bd..8f5faa60 100644 --- a/src/main/java/io/github/dsheirer/edac/CRCUtil.java +++ b/src/main/java/io/github/dsheirer/edac/CRCUtil.java @@ -161,8 +161,8 @@ public class CRCUtil // decode( message, 0, 135, polynomial, 9 ); // // mLog.debug( message.toString() ); - - long[] table = generate( 135, 9, 0x259l, 0, false, Parity.NONE ); + + long[] table = CRCUtil.generate( 135, 9, 0x259l, 0x1FF, false, Parity.NONE ); mLog.debug( toCodeArray( table ) ); mLog.debug( "Finished" ); } diff --git a/src/main/java/io/github/dsheirer/edac/Golay18.java b/src/main/java/io/github/dsheirer/edac/Golay18.java index 17a91c25..943366cf 100644 --- a/src/main/java/io/github/dsheirer/edac/Golay18.java +++ b/src/main/java/io/github/dsheirer/edac/Golay18.java @@ -1,23 +1,23 @@ package io.github.dsheirer.edac; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /******************************************************************************* * 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 * ----------------------------------------------------------------------- @@ -32,22 +32,25 @@ import org.slf4j.LoggerFactory; */ public class Golay18 { - private final static Logger mLog = LoggerFactory.getLogger( Golay18.class ); + private final static Logger mLog = LoggerFactory.getLogger(Golay18.class); - /** - * Performs error detection and correction. - */ - public static void checkAndCorrect( BinaryMessage message, int startIndex ) - { - int value = message.getInt( startIndex, startIndex + 17 ); + /** + * Performs error detection and correction. + */ + public static void checkAndCorrect(CorrectedBinaryMessage message, int startIndex) + { + int value = message.getInt(startIndex, startIndex + 17); - BinaryMessage temp = new BinaryMessage( 24 ); - temp.load( 6, 18, value ); + CorrectedBinaryMessage temp = new CorrectedBinaryMessage(24); + temp.load(6, 18, value); - BinaryMessage corrected = Golay24.checkAndCorrect( temp, 0 ); + int errorsCorrected = Golay24.checkAndCorrect(temp, 0); - int correctedValue = corrected.getInt( 6, 23 ); - - message.load( startIndex, 18, correctedValue ); - } + int correctedValue = temp.getInt(6, 23); + + message.load(startIndex, 18, correctedValue); + + int correctedBitCount = Integer.bitCount(value ^ correctedValue); + message.incrementCorrectedBitCount(correctedBitCount); + } } diff --git a/src/main/java/io/github/dsheirer/edac/Golay24.java b/src/main/java/io/github/dsheirer/edac/Golay24.java index 6d1da2bb..44ad8a50 100644 --- a/src/main/java/io/github/dsheirer/edac/Golay24.java +++ b/src/main/java/io/github/dsheirer/edac/Golay24.java @@ -1,23 +1,24 @@ package io.github.dsheirer.edac; import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /******************************************************************************* * 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 * ----------------------------------------------------------------------- @@ -30,138 +31,133 @@ import org.slf4j.LoggerFactory; */ public class Golay24 { - private final static Logger mLog = LoggerFactory.getLogger( Golay24.class ); + private final static Logger mLog = LoggerFactory.getLogger(Golay24.class); - /** - * Galois 24/12 checksums generated by: - * - * CRCUtil.generate( 12, 11, 0xC75, 0x0, true ); - */ - public static final int[] CHECKSUMS = new int[] - { - 0x63A, 0x31D, 0x7B4, 0x3DA, 0x1ED, 0x6CC, 0x366, 0x1B3, - 0x6E3, 0x54B, 0x49F, 0x475, 0x400, 0x200, 0x100, 0x080, - 0x040, 0x020, 0x010, 0x008, 0x004, 0x002, 0x001 - }; + /** + * Galois 24/12 checksums generated by: + * + * CRCUtil.generate( 12, 11, 0xC75, 0x0, true ); + */ + public static final int[] CHECKSUMS = new int[] + { + 0x63A, 0x31D, 0x7B4, 0x3DA, 0x1ED, 0x6CC, 0x366, 0x1B3, + 0x6E3, 0x54B, 0x49F, 0x475, 0x400, 0x200, 0x100, 0x080, + 0x040, 0x020, 0x010, 0x008, 0x004, 0x002, 0x001 + }; - private static int calculateChecksum( BinaryMessage message, int startIndex ) - { - int calculated = 0; //Starting value + private static int calculateChecksum(BinaryMessage message, int startIndex) + { + int calculated = 0; //Starting value - /* Iterate the set bits and XOR running checksum with lookup value */ - for (int i = message.nextSetBit( startIndex ); - i >= startIndex && i < startIndex + 12; - i = message.nextSetBit( i+1 ) ) - { - calculated ^= CHECKSUMS[ i - startIndex ]; - } - - return calculated; - } - - /** - * Performs error detection and returns a corrected copy of the 24-bit - * message that starts at the start index. - * @param message - source message containing startIndex + 24 bits length - * @param startIndex - start of the 24-bit galois 24 protected bit set - * @return - corrected 24-bit galois value - */ - public static BinaryMessage checkAndCorrect( BinaryMessage message, int startIndex ) - { - boolean parityError = message.cardinality() % 2 != 0; - - int syndrome = getSyndrome( message, startIndex ); - - /* No errors */ - if( syndrome == 0 ) - { - if( parityError ) - { - message.flip( startIndex + 23 ); - } + /* Iterate the set bits and XOR running checksum with lookup value */ + for(int i = message.nextSetBit(startIndex); + i >= startIndex && i < startIndex + 12; + i = message.nextSetBit(i + 1)) + { + calculated ^= CHECKSUMS[i - startIndex]; + } - message.setCRC( CRC.PASSED ); - - return message; - } + return calculated; + } - /* Get original message value */ - int original = message.getInt( 0, 22 ); - - int index = -1; - int syndromeWeight = 3; - int errors = 0; - - while( index < 23 ) - { - if( index != -1 ) - { - /* restore the previous flipped bit */ - if( index > 0 ) - { - message.flip( index - 1 ); - } - - message.flip( index ); - - syndromeWeight = 2; - } - - syndrome = getSyndrome( message, startIndex ); - - if( syndrome > 0 ) - { - for( int i = 0; i < 23; i++ ) - { - - errors = Integer.bitCount( syndrome ); - - if( errors <= syndromeWeight ) - { - message.xor( 12, 11, syndrome ); - - message.rotateRight( i, startIndex, startIndex + 22 ); + /** + * Performs error detection and returns a corrected copy of the 24-bit + * message that starts at the start index. + * + * @param message - source message containing startIndex + 24 bits length + * @param startIndex - start of the 24-bit galois 24 protected bit set + * @return - corrected 24-bit galois value + */ + public static int checkAndCorrect(CorrectedBinaryMessage message, int startIndex) + { + boolean parityError = message.cardinality() % 2 != 0; - if( index >= 0 ) - { - errors ++; - } + int syndrome = getSyndrome(message, startIndex); - int corrected = message.getInt( 0, 22 ); - - if( Integer.bitCount( original ^ corrected ) > 3 ) - { - message.setCRC( CRC.FAILED_CRC ); - - return message; - } - - message.setCRC( CRC.PASSED ); - - return message; - } - else - { - message.rotateLeft( startIndex, startIndex + 22 ); - syndrome = getSyndrome( message, startIndex ); - } - } - - index++; - } - } + /* No errors */ + if(syndrome == 0) + { + if(parityError) + { + message.flip(startIndex + 23); + message.incrementCorrectedBitCount(1); + return 1; + } - message.setCRC( CRC.FAILED_CRC ); - - return message; - } + return 0; + } - private static int getSyndrome( BinaryMessage message, int startIndex ) - { - int calculated = calculateChecksum( message, startIndex ); - - int checksum = message.getInt( startIndex + 12, startIndex + 22 ); + /* Get original message value */ + int original = message.getInt(0, 22); - return ( checksum ^ calculated ); - } + int index = -1; + int syndromeWeight = 3; + int errors = 0; + + while(index < 23) + { + if(index != -1) + { + /* restore the previous flipped bit */ + if(index > 0) + { + message.flip(index - 1); + } + + message.flip(index); + + syndromeWeight = 2; + } + + syndrome = getSyndrome(message, startIndex); + + if(syndrome > 0) + { + for(int i = 0; i < 23; i++) + { + + errors = Integer.bitCount(syndrome); + + if(errors <= syndromeWeight) + { + message.xor(12, 11, syndrome); + + message.rotateRight(i, startIndex, startIndex + 22); + + if(index >= 0) + { + errors++; + } + + int corrected = message.getInt(0, 22); + + if(Integer.bitCount(original ^ corrected) > 3) + { + return 2; + } + + return 1; + } + else + { + message.rotateLeft(startIndex, startIndex + 22); + syndrome = getSyndrome(message, startIndex); + } + } + + index++; + } + } + + return 2; + } + + private static int getSyndrome(BinaryMessage message, int startIndex) + { + int calculated = calculateChecksum(message, startIndex); + + int checksum = message.getInt(startIndex + 12, startIndex + 22); + + return (checksum ^ calculated); + } } diff --git a/src/main/java/io/github/dsheirer/edac/Hamming10.java b/src/main/java/io/github/dsheirer/edac/Hamming10.java index 979e10dd..9862f3b2 100644 --- a/src/main/java/io/github/dsheirer/edac/Hamming10.java +++ b/src/main/java/io/github/dsheirer/edac/Hamming10.java @@ -1,115 +1,124 @@ package io.github.dsheirer.edac; import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Hamming (10,6,3) error detection and correction utility. This is a truncated version of the Hamming15 class. + */ public class Hamming10 { - private final static Logger mLog = LoggerFactory.getLogger( Hamming10.class ); - - private static int[] CHECKSUMS = new int[] { 0xE,0xD,0xB,0X7,0x3,0xC }; + private final static Logger mLog = LoggerFactory.getLogger(Hamming10.class); - /** - * Performs error detection and correction of any single-bit errors. This - * is a truncated version of the Hamming15 class. - * - * @param frame - binary frame containing a Hamming(10,6,3) protected field - * - * @param startIndex - offset to the first bit of the field - * - * @return - 0 = no errors - * 1 = a single-bit error was corrected - */ - public static int checkAndCorrect( BinaryMessage frame, int startIndex ) - { - int syndrome = getSyndrome( frame, startIndex ); + private static int[] CHECKSUMS = new int[]{0xE, 0xD, 0xB, 0X7, 0x3, 0xC}; - switch( syndrome ) - { - case 0: - return 0; - case 1: - frame.flip( startIndex + 9 ); //Parity 1 - return 1; - case 2: - frame.flip( startIndex + 8 ); //Parity 2 - return 1; - case 3: - frame.flip( startIndex + 4 ); //Data 2 - return 1; - case 4: - frame.flip( startIndex + 7 ); //Parity 4 - return 1; - case 5: - return 2; - case 6: - return 2; - case 7: - frame.flip( startIndex + 3 ); //Data 3 - return 1; - case 8: - frame.flip( startIndex + 6 ); //Parity 8 - return 1; - case 9: - return 2; - case 10: - return 2; - case 11: - frame.flip( startIndex + 2 ); //Data 4 - return 1; - case 12: - frame.flip( startIndex + 5 ); //Data 1 - return 1; - case 13: - frame.flip( startIndex + 1 ); //Data 5 - return 1; - case 14: - frame.flip( startIndex + 0 ); //Data 6 - return 1; - case 15: - return 2; - } + /** + * Performs error detection and correction of any single-bit errors. + * + * @param frame - binary frame containing a Hamming(10,6,3) protected field + * @param startIndex - offset to the first bit of the field + * @return - 0 = no errors + * 1 = a single-bit error was corrected + * 2 = more than 1 bit error detected - unable to correct word + */ + public static int checkAndCorrect(CorrectedBinaryMessage frame, int startIndex) + { + int syndrome = getSyndrome(frame, startIndex); - /* We'll never get to here */ - return 2; - } + switch(syndrome) + { + case 0: + return 0; + case 1: + frame.flip(startIndex + 9); //Parity 1 + frame.incrementCorrectedBitCount(1); + return 1; + case 2: + frame.flip(startIndex + 8); //Parity 2 + frame.incrementCorrectedBitCount(1); + return 1; + case 3: + frame.flip(startIndex + 4); //Data 2 + frame.incrementCorrectedBitCount(1); + return 1; + case 4: + frame.flip(startIndex + 7); //Parity 4 + frame.incrementCorrectedBitCount(1); + return 1; + case 5: + return 2; + case 6: + return 2; + case 7: + frame.flip(startIndex + 3); //Data 3 + frame.incrementCorrectedBitCount(1); + return 1; + case 8: + frame.flip(startIndex + 6); //Parity 8 + frame.incrementCorrectedBitCount(1); + return 1; + case 9: + return 2; + case 10: + return 2; + case 11: + frame.flip(startIndex + 2); //Data 4 + frame.incrementCorrectedBitCount(1); + return 1; + case 12: + frame.flip(startIndex + 5); //Data 1 + frame.incrementCorrectedBitCount(1); + return 1; + case 13: + frame.flip(startIndex + 1); //Data 5 + frame.incrementCorrectedBitCount(1); + return 1; + case 14: + frame.flip(startIndex + 0); //Data 6 + frame.incrementCorrectedBitCount(1); + return 1; + case 15: + return 2; + } - /** - * Calculates the checksum (Parity 8,4,2,1) for data (6 <> 1 ) bits. - * @param frame - frame containing hamming(10) protected word - * @param startIndex - start bit index of the hamming protected word - * @return parity value, 0 - 15 - */ - private static int calculateChecksum( BinaryMessage frame, int startIndex ) - { - int calculated = 0; //Starting value + /* We'll never get to here */ + return 2; + } - /* Iterate the set bits and XOR running checksum with lookup value */ - for (int i = frame.nextSetBit( startIndex ); - i >= startIndex && i < startIndex + 6; - i = frame.nextSetBit( i+1 ) ) - { - calculated ^= CHECKSUMS[ i - startIndex ]; - } - - return calculated; - } + /** + * Calculates the checksum (Parity 8,4,2,1) for data (6 <> 1 ) bits. + * + * @param frame - frame containing hamming(10) protected word + * @param startIndex - start bit index of the hamming protected word + * @return parity value, 0 - 15 + */ + private static int calculateChecksum(BinaryMessage frame, int startIndex) + { + int calculated = 0; //Starting value - /** - * Calculates the syndrome - xor of the calculated checksum and the actual - * checksum. - * - * @param frame - binary frame containing a hamming(15,11,4) protected word - * @param startIndex - of bit 0 of the hamming protected word - * @return - 0 (no errors) or 1 (single bit error corrected) - */ - private static int getSyndrome( BinaryMessage frame, int startIndex ) - { - int calculated = calculateChecksum( frame, startIndex ); - - int checksum = frame.getInt( startIndex + 6, startIndex + 9 ); + /* Iterate the set bits and XOR running checksum with lookup value */ + for(int i = frame.nextSetBit(startIndex); i >= startIndex && i < startIndex + 6; i = frame.nextSetBit(i + 1)) + { + calculated ^= CHECKSUMS[i - startIndex]; + } - return ( checksum ^ calculated ); - } + return calculated; + } + + /** + * Calculates the syndrome - xor of the calculated checksum and the actual + * checksum. + * + * @param frame - binary frame containing a hamming(15,11,4) protected word + * @param startIndex - of bit 0 of the hamming protected word + * @return - 0 (no errors) or 1 (single bit error corrected) + */ + private static int getSyndrome(BinaryMessage frame, int startIndex) + { + int calculated = calculateChecksum(frame, startIndex); + int checksum = frame.getInt(startIndex + 6, startIndex + 9); + return (checksum ^ calculated); + } } diff --git a/src/main/java/io/github/dsheirer/edac/ReedSolomon_63_47_17.java b/src/main/java/io/github/dsheirer/edac/ReedSolomon_63_47_17.java index 83bb510e..ad2189a8 100644 --- a/src/main/java/io/github/dsheirer/edac/ReedSolomon_63_47_17.java +++ b/src/main/java/io/github/dsheirer/edac/ReedSolomon_63_47_17.java @@ -1,38 +1,40 @@ -package io.github.dsheirer.edac; +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ -/******************************************************************************* - * 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 - ******************************************************************************/ +package io.github.dsheirer.edac; public class ReedSolomon_63_47_17 extends BerlekempMassey_63 { - /** - * Reed-Solomon RS(63,47,17) decoder. This can also be used for error detection - * and correction of the following RS codes: - * - * RS(36,20,17) - max 8 errors - * RS(24,16,9) - max 4 errors - * RS(24,12,13) - max 6 errors - * - * The maximum correctable errors is determined by (n-k)/2, or hamming - * distance divided by 2. - */ - public ReedSolomon_63_47_17( int maximumCorrectableErrors ) + /** + * Reed-Solomon RS(63,47,17) decoder. This can also be used for error detection + * and correction of the following RS codes: + * + * RS(36,20,17) - max 8 errors + * RS(24,16,9) - max 4 errors + * RS(24,12,13) - max 6 errors + * + * The maximum correctable errors is determined by (n-k)/2, or hamming + * distance divided by 2. + */ + public ReedSolomon_63_47_17(int maximumCorrectableErrors) { - super( maximumCorrectableErrors ); + super(maximumCorrectableErrors); } } diff --git a/src/main/java/io/github/dsheirer/edac/trellis/DMRNode.java b/src/main/java/io/github/dsheirer/edac/trellis/DMR_3_4_Node.java similarity index 96% rename from src/main/java/io/github/dsheirer/edac/trellis/DMRNode.java rename to src/main/java/io/github/dsheirer/edac/trellis/DMR_3_4_Node.java index bd4255e2..b04f90c8 100644 --- a/src/main/java/io/github/dsheirer/edac/trellis/DMRNode.java +++ b/src/main/java/io/github/dsheirer/edac/trellis/DMR_3_4_Node.java @@ -18,7 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.edac.trellis; -public class DMRNode extends Node +public class DMR_3_4_Node extends Node { //Hamming error values indicating the number of bits set in each indexed value, 0-15 public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; @@ -47,7 +47,7 @@ public class DMRNode extends Node * @param inputValue for the trellis time instant * @param transmittedOutputValue the actual four-bit output value that was transmitted. */ - public DMRNode(int inputValue, int transmittedOutputValue) + public DMR_3_4_Node(int inputValue, int transmittedOutputValue) { super(inputValue, transmittedOutputValue); } diff --git a/src/main/java/io/github/dsheirer/edac/trellis/P25_1_2_Node.java b/src/main/java/io/github/dsheirer/edac/trellis/P25_1_2_Node.java new file mode 100644 index 00000000..59e25145 --- /dev/null +++ b/src/main/java/io/github/dsheirer/edac/trellis/P25_1_2_Node.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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.edac.trellis; + +public class P25_1_2_Node extends Node +{ + /** + * Hamming error values indicating the number of bits set in each indexed value, 0-15. XOR the + * transmitted 4-bit value against the reference 4-bit value to obtain the lookup index to this array. + */ + public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; + + /** + * P25 Finite State Machine 1/2 Rate transition matrix. + * + * Note: these transmitted bit values are translated from TIA-102 BAAA Table 7-2 encoder state + * table constellation values. + */ + public static final int[][] TRANSITION_MATRIX = new int[][] + { + {2,12,1,15},//0,15,12,3 + {14,0,13,3},//4,11,8,7 + {9,7,10,4}, //13,2,1,14 + {5,11,6,8} //9,6,5,10 + }; + + /** + * P25 1/2-rate Trellis Coded Modulation (TCM) node + * + * @param inputValue for the trellis time instant + * @param transmittedOutputValue the actual four-bit output value that was transmitted. + */ + public P25_1_2_Node(int inputValue, int transmittedOutputValue) + { + super(inputValue, transmittedOutputValue); + } + + /** + * Calculates the error between the preceeding node and this node as the Hamming distance between the expected + * four-bit output value versus the actual transmitted output value. + * + * @param precedingNode that precedes this node + * @return error value for this node relative to the preceeding node argument + */ + @Override + public int getError(Node precedingNode) + { + if(precedingNode != null) + { + int expectedOutputValue = getOutputValue(precedingNode); + int errorMask = expectedOutputValue ^ getTransmittedOutputValue(); + return HAMMING_ERROR_COUNT[errorMask]; + } + + return 0; + } + + /** + * Expected output four-bit value when this node is preceeded by the argument node. + * + * @param precedingNode that precedes this node + * @return expected output value + */ + @Override + public int getOutputValue(Node precedingNode) + { + return TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()]; + } +} diff --git a/src/main/java/io/github/dsheirer/edac/trellis/P25_3_4_Node.java b/src/main/java/io/github/dsheirer/edac/trellis/P25_3_4_Node.java new file mode 100644 index 00000000..0654961b --- /dev/null +++ b/src/main/java/io/github/dsheirer/edac/trellis/P25_3_4_Node.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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.edac.trellis; + +public class P25_3_4_Node extends Node +{ + //Hamming error values indicating the number of bits set in each indexed value, 0-15 + public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; + + /** + * Trellis encoder state transition table. Note: this table is converted from the ICD state + * transition table where the table symbol is converted to the transmitted bit value. + */ + public static final int[][] TRANSITION_MATRIX = new int[][] + { + {2,13,14,1,7,8,11,4}, + {14,1,7,8,11,4,2,13}, + {10,5,6,9,15,0,3,12}, + {6,9,15,0,3,12,10,5}, + {15,0,3,12,10,5,6,9}, + {3,12,10,5,6,9,15,0}, + {7,8,11,4,2,13,14,1}, + {11,4,2,13,14,1,7,8} + }; + + /** + * Trellis Coded Modulation (TCM) 3/4-rate trellis node for decoding P25 + * + * @param inputValue for the trellis time instant + * @param transmittedOutputValue the actual four-bit output value that was transmitted. + */ + public P25_3_4_Node(int inputValue, int transmittedOutputValue) + { + super(inputValue, transmittedOutputValue); + } + + /** + * Calculates the error between the preceeding node and this node as the Hamming distance between the expected + * four-bit output value versus the actual transmitted output value. + * + * @param precedingNode that precedes this node + * @return error value for this node relative to the preceeding node argument + */ + @Override + public int getError(Node precedingNode) + { + if(precedingNode != null) + { + int expectedOutputValue = getOutputValue(precedingNode); + int errorMask = expectedOutputValue ^ getTransmittedOutputValue(); + return HAMMING_ERROR_COUNT[errorMask]; + } + + return 0; + } + + /** + * Expected output four-bit value when this node is preceeded by the argument node. + * + * @param precedingNode that precedes this node + * @return expected output value + */ + @Override + public int getOutputValue(Node precedingNode) + { + return TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()]; + } +} diff --git a/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_1_2_P25.java b/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_1_2_P25.java new file mode 100644 index 00000000..56a9e8dc --- /dev/null +++ b/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_1_2_P25.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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.edac.trellis; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +import java.util.List; + +/** + * Viterbi decoder for APCO-25 1/2 rate Trellis Coded Modulation (TCM) encoded messages. + */ +public class ViterbiDecoder_1_2_P25 extends ViterbiDecoder +{ + public ViterbiDecoder_1_2_P25() + { + super(2,4); + } + + /** + * Decodes a 1/2 rate trellis coded modulation (TCM) encoded P25 binary message containing 196 bits that have + * already been deinterleaved. + * + * @param encodedMessage to decode that has already been deinterleaved. + * @return decoded message + */ + public CorrectedBinaryMessage decode(BinaryMessage encodedMessage) + { + int[] symbols = getSymbols(encodedMessage); + + Path mostLikelyPath = decode(symbols); + + return getMessage(mostLikelyPath); + } + + /** + * Extracts the decoded/corrected message from the surviving path argument. + * + * @param path to extract a message from + * @return corrected binary message. + */ + private CorrectedBinaryMessage getMessage(Path path) + { + List mNodes = path.getNodes(); + + //Each node contains an input value excluding the starting(0) and final flushing(0) nodes. + CorrectedBinaryMessage message = new CorrectedBinaryMessage((mNodes.size() - 2) * getInputBitLength()); + + if(mNodes.size() > 2) + { + for(int x = 1; x < mNodes.size() - 1; x++) + { + int inputValue = mNodes.get(x).getInputValue(); + + int messageOffset = getInputBitLength() * (x - 1); + + if((inputValue & 2) == 2) + { + message.set(messageOffset); + } + + if((inputValue & 1) == 1) + { + message.set(messageOffset + 1); + } + } + } + + //Transfer the corrected error count to the message + message.setCorrectedBitCount(path.getError()); + + return message; + } + + /** + * Extracts (49) four-bit symbols (196 bits / 4) from the deinterleaved P25 binary message + * + * @param encodedMessage that deinterleaved and contains 196-bits of 1/2 TCM encoding + * @return symbols + */ + public int[] getSymbols(BinaryMessage encodedMessage) + { + //Ensure we have an integral number of transmitted symbols (nibbles) in the message + if(encodedMessage.size() % getOutputBitLength() != 0) + { + throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" + + " message size: " + encodedMessage.size()); + } + + int[] symbols = new int[encodedMessage.size() / getOutputBitLength()]; + + int index = 0; + int pointer = 0; + + while(index < encodedMessage.size()) + { + int symbol = encodedMessage.getInt(index, index + (getOutputBitLength() - 1)); + symbols[pointer++] = symbol; + index += getOutputBitLength(); + } + + return symbols; + } + + /** + * Creates a P25 node + * @param inputValue for the specific trellis node + * @param transmittedOutputValue the actual transmitted output value + * @return created node + */ + @Override + protected Node createNode(int inputValue, int transmittedOutputValue) + { + return new P25_1_2_Node(inputValue, transmittedOutputValue); + } + + /** + * Creates a P25 flushing node. P25 uses an input value of zero to flush the final output value out of the + * encoder. + * + * @param transmittedOutputValue the actual transmitted output value + * @return + */ + @Override + protected Node createFlushingNode(int transmittedOutputValue) + { + return new P25_1_2_Node(0, transmittedOutputValue); + } + + /** + * Creates a P25 starting node. P25 uses a starting input value of zero for the initial node. + */ + @Override + protected Node createStartingNode() + { + return new P25_1_2_Node(0,0); + } +} diff --git a/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_DMR.java b/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_DMR.java index e4586cfb..28c87cfd 100644 --- a/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_DMR.java +++ b/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_DMR.java @@ -40,7 +40,7 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder * @param encodedMessage to decode that has already been deinterleaved. * @return decoded message */ - public BinaryMessage decode(BinaryMessage encodedMessage) + public CorrectedBinaryMessage decode(BinaryMessage encodedMessage) { int[] symbols = getSymbols(encodedMessage); @@ -132,7 +132,7 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder @Override protected Node createNode(int inputValue, int transmittedOutputValue) { - return new DMRNode(inputValue, transmittedOutputValue); + return new DMR_3_4_Node(inputValue, transmittedOutputValue); } /** @@ -145,7 +145,7 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder @Override protected Node createFlushingNode(int transmittedOutputValue) { - return new DMRNode(0, transmittedOutputValue); + return new DMR_3_4_Node(0, transmittedOutputValue); } /** @@ -154,6 +154,6 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder @Override protected Node createStartingNode() { - return new DMRNode(0,0); + return new DMR_3_4_Node(0,0); } } diff --git a/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_P25.java b/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_P25.java new file mode 100644 index 00000000..97a2a44d --- /dev/null +++ b/src/main/java/io/github/dsheirer/edac/trellis/ViterbiDecoder_3_4_P25.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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.edac.trellis; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +import java.util.List; + +public class ViterbiDecoder_3_4_P25 extends ViterbiDecoder +{ + /** + * Viterbi decoder for P25 3/4 rate Trellis Coded Modulation (TCM) encoded messages. + */ + public ViterbiDecoder_3_4_P25() + { + super(3, 4); + } + + /** + * Decodes a 3/4 rate trellis coded modulation (TCM) encoded P25 binary message containing 196 bits that have + * already been deinterleaved. + * + * @param encodedMessage to decode that has already been deinterleaved. + * @return decoded message + */ + public CorrectedBinaryMessage decode(BinaryMessage encodedMessage) + { + int[] symbols = getSymbols(encodedMessage); + + Path mostLikelyPath = decode(symbols); + + return getMessage(mostLikelyPath); + } + + /** + * Extracts the decoded/corrected message from the surviving path argument. + * + * @param path to extract a message from + * @return corrected binary message. + */ + private static CorrectedBinaryMessage getMessage(Path path) + { + List mNodes = path.getNodes(); + + //Each node contains an input value excluding the starting(0) and final flushing(0) nodes. + CorrectedBinaryMessage message = new CorrectedBinaryMessage((mNodes.size() - 2) * 3); + + if(mNodes.size() > 2) + { + for(int x = 1; x < mNodes.size() - 1; x++) + { + int inputValue = mNodes.get(x).getInputValue(); + + int messageOffset = 3 * (x - 1); + + if((inputValue & 4) == 4) + { + message.set(messageOffset); + } + + if((inputValue & 2) == 2) + { + message.set(messageOffset + 1); + } + + if((inputValue & 1) == 1) + { + message.set(messageOffset + 2); + } + } + } + + //Transfer the corrected error count to the message + message.setCorrectedBitCount(path.getError()); + + return message; + } + + /** + * Extracts (49) four-bit symbols (196 bits / 4) from the deinterleaved P25 binary message + * + * @param encodedMessage that deinterleaved and contains 196-bits of 3/4 TCM encoding + * @return symbols + */ + public int[] getSymbols(BinaryMessage encodedMessage) + { + //Ensure we have an integral number of transmitted symbols (nibbles) in the message + if(encodedMessage.size() % getOutputBitLength() != 0) + { + throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" + + " message size: " + encodedMessage.size()); + } + + int[] symbols = new int[encodedMessage.size() / getOutputBitLength()]; + + int index = 0; + int pointer = 0; + + while(index < encodedMessage.size()) + { + int symbol = encodedMessage.getInt(index, index + (getOutputBitLength() - 1)); + symbols[pointer++] = symbol; + index += getOutputBitLength(); + } + + return symbols; + } + + /** + * Creates a P25 node + * + * @param inputValue for the specific trellis node + * @param transmittedOutputValue the actual transmitted output value + * @return created node + */ + @Override + protected Node createNode(int inputValue, int transmittedOutputValue) + { + return new P25_3_4_Node(inputValue, transmittedOutputValue); + } + + /** + * Creates a P25 flushing node. P25 uses an input value of zero to flush the final output value out of the + * encoder. + * + * @param transmittedOutputValue the actual transmitted output value + * @return + */ + @Override + protected Node createFlushingNode(int transmittedOutputValue) + { + return new P25_3_4_Node(0, transmittedOutputValue); + } + + /** + * Creates a P25 starting node. P25 uses a starting input value of zero for the initial node. + */ + @Override + protected Node createStartingNode() + { + return new P25_3_4_Node(0, 0); + } +} diff --git a/src/main/java/io/github/dsheirer/eventbus/MyEventBus.java b/src/main/java/io/github/dsheirer/eventbus/MyEventBus.java new file mode 100644 index 00000000..ea34d7f7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/eventbus/MyEventBus.java @@ -0,0 +1,36 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.eventbus; + +import com.google.common.eventbus.EventBus; + +/** + * System wide event bus for dispatching/broadcasting system wide events or objects + */ +public class MyEventBus +{ + private static final EventBus EVENT_BUS = new EventBus(); + + public static EventBus getEventBus() + { + return EVENT_BUS; + } +} diff --git a/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java b/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java index 49ebdc38..009d1db7 100644 --- a/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java +++ b/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java @@ -2,14 +2,20 @@ package io.github.dsheirer.filter; import net.miginfocom.swing.MigLayout; -import javax.swing.*; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.SwingUtilities; import javax.swing.event.CellEditorListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeSelectionModel; -import java.awt.*; +import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; @@ -25,376 +31,391 @@ import java.util.List; public class FilterEditorPanel extends JPanel { private static final long serialVersionUID = 1L; - + private JTree mTree; private DefaultTreeModel mModel; - private FilterSet mFilterSet; - - public FilterEditorPanel( FilterSet filterSet ) - { - setLayout( new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]") ); - - mFilterSet = filterSet; - - init(); - } - - private void init() - { - DefaultMutableTreeNode root = new DefaultMutableTreeNode( mFilterSet ); - - mModel = new DefaultTreeModel( root ); - - addFilterSet( mFilterSet, root ); - - mTree = new JTree( mModel ); - mTree.setShowsRootHandles( true ); - - mTree.addMouseListener( new MouseHandler() ); + private FilterSet mFilterSet; - mTree.getSelectionModel().setSelectionMode( - TreeSelectionModel.SINGLE_TREE_SELECTION ); + public FilterEditorPanel(FilterSet filterSet) + { + setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - mTree.setCellRenderer( new EditorTreeCellRenderer() ); + mFilterSet = filterSet; - mTree.setCellEditor( new FilterTreeNodeEditor() ); - - mTree.setEditable( true ); - - add( mTree ); - } - - private void addFilterSet( FilterSet filterSet, DefaultMutableTreeNode parent ) - { - List> filters = filterSet.getFilters(); + init(); + } - /* sort the filters in alphabetical order by name */ - Collections.sort( filters, new Comparator>() - { - @Override - public int compare( IFilter first, IFilter second ) - { - return first.getName().compareTo( second.getName() ); - } - }); - - for( IFilter filter: filters ) - { - DefaultMutableTreeNode child = new DefaultMutableTreeNode( filter ); + private void init() + { + DefaultMutableTreeNode root = new DefaultMutableTreeNode(mFilterSet); - mModel.insertNodeInto( child, parent, parent.getChildCount() ); - - if( filter instanceof FilterSet ) - { - addFilterSet( (FilterSet)filter, child ); - } - else if( filter instanceof Filter ) - { - addFilter( (Filter)filter, child ); - } - } - } - - private void addFilter( Filter filter, DefaultMutableTreeNode parent ) - { - List> elements = filter.getFilterElements(); - - Collections.sort( elements, new Comparator>() - { - @Override - public int compare( FilterElement o1, FilterElement o2 ) - { - return o1.toString().compareTo( o2.toString() ); - } - } ); - - for( FilterElement element: elements ) - { - DefaultMutableTreeNode child = new DefaultMutableTreeNode( element ); + mModel = new DefaultTreeModel(root); - mModel.insertNodeInto( child, parent, parent.getChildCount() ); - } - } - - public class MouseHandler implements MouseListener - { - public MouseHandler() - { - } + addFilterSet(mFilterSet, root); - @Override - public void mouseClicked( MouseEvent event ) + mTree = new JTree(mModel); + mTree.setShowsRootHandles(true); + + mTree.addMouseListener(new MouseHandler()); + + mTree.getSelectionModel().setSelectionMode( + TreeSelectionModel.SINGLE_TREE_SELECTION); + + mTree.setCellRenderer(new EditorTreeCellRenderer()); + + mTree.setCellEditor(new FilterTreeNodeEditor()); + + mTree.setEditable(true); + + add(mTree); + } + + private void addFilterSet(FilterSet filterSet, DefaultMutableTreeNode parent) + { + List> filters = filterSet.getFilters(); + + /* sort the filters in alphabetical order by name */ + Collections.sort(filters, new Comparator>() { - if( SwingUtilities.isRightMouseButton( event ) ) - { - int row = mTree.getRowForLocation( event.getX(), - event.getY() ); - - if( row != -1 ) - { - mTree.setSelectionRow( row ); - - Object selectedNode = - mTree.getLastSelectedPathComponent(); - - if( selectedNode instanceof DefaultMutableTreeNode ) - { - final DefaultMutableTreeNode node = - (DefaultMutableTreeNode)selectedNode; - - if( node.getChildCount() > 0 ) - { - JPopupMenu selectionMenu = new JPopupMenu(); + @Override + public int compare(IFilter first, IFilter second) + { + return first.getName().compareTo(second.getName()); + } + }); - JMenuItem selectAll = new JMenuItem( "Select All" ); - selectAll.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - setFilterEnabled( mModel, node, true ); - } - }); - - selectionMenu.add( selectAll ); - - JMenuItem deselectAll = new JMenuItem( "Deselect All" ); - deselectAll.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - setFilterEnabled( mModel, node, false ); - } - }); + for(IFilter filter : filters) + { + DefaultMutableTreeNode child = new DefaultMutableTreeNode(filter); - selectionMenu.add( deselectAll ); - - selectionMenu.show( mTree, - event.getX(), - event.getY() ); - } - } - } - } + mModel.insertNodeInto(child, parent, parent.getChildCount()); + + if(filter instanceof FilterSet) + { + addFilterSet((FilterSet) filter, child); + } + else if(filter instanceof Filter) + { + addFilter((Filter) filter, child); + } + } + } + + private void addFilter(Filter filter, DefaultMutableTreeNode parent) + { + List> elements = filter.getFilterElements(); + + Collections.sort(elements, new Comparator>() + { + @Override + public int compare(FilterElement o1, FilterElement o2) + { + return o1.toString().compareTo(o2.toString()); + } + }); + + for(FilterElement element : elements) + { + DefaultMutableTreeNode child = new DefaultMutableTreeNode(element); + + mModel.insertNodeInto(child, parent, parent.getChildCount()); + } + } + + public class MouseHandler implements MouseListener + { + public MouseHandler() + { } - @Override - public void mousePressed( MouseEvent e ){} - @Override - public void mouseReleased( MouseEvent e ){} - @Override - public void mouseEntered( MouseEvent e ){} - @Override - public void mouseExited( MouseEvent e ){} - } - - /** - * Recursively sets the enabled state on all filter node children below the - * specified parent node. - * - * @param model - model containing the tree nodes - * @param node - parent node - * @param enabled - true or false to enable or disable the filters - */ - private void setFilterEnabled( DefaultTreeModel model, - DefaultMutableTreeNode node, - boolean enabled ) - { - Object obj = node.getUserObject(); - - if( obj instanceof FilterSet ) - { - ((FilterSet)obj).setEnabled( enabled ); - } - else if( obj instanceof Filter ) - { - ((Filter)obj).setEnabled( enabled ); - } - else if( obj instanceof FilterElement ) - { - ((FilterElement)obj).setEnabled( enabled ); - } - - model.nodeChanged( node ); - - /* Recursively set the children of this node */ - Enumeration children = node.children(); - - while( children.hasMoreElements() ) - { - Object child = children.nextElement(); - - if( child instanceof DefaultMutableTreeNode ) - { - setFilterEnabled( model, (DefaultMutableTreeNode)child, enabled ); - } - } - } - - public class EditorTreeCellRenderer extends DefaultTreeCellRenderer - { - private static final long serialVersionUID = 1L; - - public Component getTreeCellRendererComponent( JTree tree, - Object treeNode, - boolean selected, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus ) - { - if( treeNode instanceof DefaultMutableTreeNode ) - { - Object userObject = ((DefaultMutableTreeNode)treeNode).getUserObject(); - - JCheckBox checkBox = null; - - if( userObject instanceof IFilter ) - { - IFilter filter = (IFilter)userObject; - - checkBox = new JCheckBox( filter.getName() ); - checkBox.setSelected( filter.isEnabled() ); - } - else if( userObject instanceof FilterElement ) - { - FilterElement element = (FilterElement)userObject; - - checkBox = new JCheckBox( element.getName() ); - checkBox.setSelected( element.isEnabled() ); - } - - if( checkBox != null ) - { - if( selected ) - { - checkBox.setForeground( getTextSelectionColor() ); - checkBox.setBackground( getBackgroundSelectionColor() ); - } - else - { - checkBox.setForeground( getTextNonSelectionColor() ); - checkBox.setBackground( getBackgroundNonSelectionColor() ); - } - - return checkBox; - } - } - - return super.getTreeCellRendererComponent( tree, treeNode, selected, - expanded, leaf, row, hasFocus ); - } - } - - public class FilterCheckBox extends JCheckBox - { - private static final long serialVersionUID = 1L; - - private IFilter mFilter; - - public FilterCheckBox( IFilter filter ) - { - super( filter.getName() ); - - mFilter = filter; - - setSelected( mFilter.isEnabled() ); - - addItemListener( new ItemListener() - { - @Override - public void itemStateChanged( ItemEvent e ) - { - mFilter.setEnabled( FilterCheckBox.this.isSelected() ); - } - } ); - } - } - - public class FilterElementCheckBox extends JCheckBox - { - private static final long serialVersionUID = 1L; - - private FilterElement mFilter; - - public FilterElementCheckBox( FilterElement filter ) - { - super( filter.getName() ); - - mFilter = filter; - - setSelected( mFilter.isEnabled() ); - - addItemListener( new ItemListener() - { - @Override - public void itemStateChanged( ItemEvent arg0 ) - { - mFilter.setEnabled( FilterElementCheckBox.this.isSelected() ); - } - } ); - } - } - - public class FilterTreeNodeEditor implements TreeCellEditor - { @Override - @SuppressWarnings( { "rawtypes", "unchecked" } ) - public Component getTreeCellEditorComponent( JTree tree, Object node, - boolean isSelected, boolean expanded, boolean leaf, int row ) + public void mouseClicked(MouseEvent event) { - if( node instanceof DefaultMutableTreeNode ) - { - Object userObject = ((DefaultMutableTreeNode)node).getUserObject(); - - if( userObject instanceof IFilter ) - { - return new FilterCheckBox( (IFilter)userObject ); - } - else if( userObject instanceof FilterElement ) - { - return new FilterElementCheckBox( (FilterElement)userObject ); - } - } - - return new JLabel( node.toString() ); + if(SwingUtilities.isRightMouseButton(event)) + { + int row = mTree.getRowForLocation(event.getX(), + event.getY()); + + if(row != -1) + { + mTree.setSelectionRow(row); + + Object selectedNode = + mTree.getLastSelectedPathComponent(); + + if(selectedNode instanceof DefaultMutableTreeNode) + { + final DefaultMutableTreeNode node = + (DefaultMutableTreeNode) selectedNode; + + if(node.getChildCount() > 0) + { + JPopupMenu selectionMenu = new JPopupMenu(); + + JMenuItem selectAll = new JMenuItem("Select All"); + selectAll.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent arg0) + { + setFilterEnabled(mModel, node, true); + } + }); + + selectionMenu.add(selectAll); + + JMenuItem deselectAll = new JMenuItem("Deselect All"); + deselectAll.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent arg0) + { + setFilterEnabled(mModel, node, false); + } + }); + + selectionMenu.add(deselectAll); + + selectionMenu.show(mTree, + event.getX(), + event.getY()); + } + } + } + } } - @Override - public void addCellEditorListener( CellEditorListener l ) {} + @Override + public void mousePressed(MouseEvent e) + { + } - @Override + @Override + public void mouseReleased(MouseEvent e) + { + } + + @Override + public void mouseEntered(MouseEvent e) + { + } + + @Override + public void mouseExited(MouseEvent e) + { + } + } + + /** + * Recursively sets the enabled state on all filter node children below the + * specified parent node. + * + * @param model - model containing the tree nodes + * @param node - parent node + * @param enabled - true or false to enable or disable the filters + */ + private void setFilterEnabled(DefaultTreeModel model, + DefaultMutableTreeNode node, + boolean enabled) + { + Object obj = node.getUserObject(); + + if(obj instanceof FilterSet) + { + ((FilterSet) obj).setEnabled(enabled); + } + else if(obj instanceof Filter) + { + ((Filter) obj).setEnabled(enabled); + } + else if(obj instanceof FilterElement) + { + ((FilterElement) obj).setEnabled(enabled); + } + + model.nodeChanged(node); + + /* Recursively set the children of this node */ + Enumeration children = node.children(); + + while(children.hasMoreElements()) + { + Object child = children.nextElement(); + + if(child instanceof DefaultMutableTreeNode) + { + setFilterEnabled(model, (DefaultMutableTreeNode) child, enabled); + } + } + } + + public class EditorTreeCellRenderer extends DefaultTreeCellRenderer + { + private static final long serialVersionUID = 1L; + + public Component getTreeCellRendererComponent(JTree tree, + Object treeNode, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) + { + if(treeNode instanceof DefaultMutableTreeNode) + { + Object userObject = ((DefaultMutableTreeNode) treeNode).getUserObject(); + + JCheckBox checkBox = null; + + if(userObject instanceof IFilter) + { + IFilter filter = (IFilter) userObject; + + checkBox = new JCheckBox(filter.getName()); + checkBox.setSelected(filter.isEnabled()); + } + else if(userObject instanceof FilterElement) + { + FilterElement element = (FilterElement) userObject; + + checkBox = new JCheckBox(element.getName()); + checkBox.setSelected(element.isEnabled()); + } + + if(checkBox != null) + { + if(selected) + { + checkBox.setForeground(getTextSelectionColor()); + checkBox.setBackground(getBackgroundSelectionColor()); + } + else + { + checkBox.setForeground(getTextNonSelectionColor()); + checkBox.setBackground(getBackgroundNonSelectionColor()); + } + + return checkBox; + } + } + + return super.getTreeCellRendererComponent(tree, treeNode, selected, + expanded, leaf, row, hasFocus); + } + } + + public class FilterCheckBox extends JCheckBox + { + private static final long serialVersionUID = 1L; + + private IFilter mFilter; + + public FilterCheckBox(IFilter filter) + { + super(filter.getName()); + + mFilter = filter; + + setSelected(mFilter.isEnabled()); + + addItemListener(new ItemListener() + { + @Override + public void itemStateChanged(ItemEvent e) + { + mFilter.setEnabled(FilterCheckBox.this.isSelected()); + } + }); + } + } + + public class FilterElementCheckBox extends JCheckBox + { + private static final long serialVersionUID = 1L; + + private FilterElement mFilter; + + public FilterElementCheckBox(FilterElement filter) + { + super(filter.getName()); + + mFilter = filter; + + setSelected(mFilter.isEnabled()); + + addItemListener(new ItemListener() + { + @Override + public void itemStateChanged(ItemEvent arg0) + { + mFilter.setEnabled(FilterElementCheckBox.this.isSelected()); + } + }); + } + } + + public class FilterTreeNodeEditor implements TreeCellEditor + { + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public Component getTreeCellEditorComponent(JTree tree, Object node, + boolean isSelected, boolean expanded, boolean leaf, int row) + { + if(node instanceof DefaultMutableTreeNode) + { + Object userObject = ((DefaultMutableTreeNode) node).getUserObject(); + + if(userObject instanceof IFilter) + { + return new FilterCheckBox((IFilter) userObject); + } + else if(userObject instanceof FilterElement) + { + return new FilterElementCheckBox((FilterElement) userObject); + } + } + + return new JLabel(node.toString()); + } + + @Override + public void addCellEditorListener(CellEditorListener l) + { + } + + @Override public void cancelCellEditing() { - + } - @Override + @Override public Object getCellEditorValue() { - return null; + return null; } - @Override - public boolean isCellEditable( EventObject anEvent ) + @Override + public boolean isCellEditable(EventObject anEvent) { - return true; + return true; } - @Override - public void removeCellEditorListener( CellEditorListener l ) {} - - @Override - public boolean shouldSelectCell( EventObject anEvent ) + @Override + public void removeCellEditorListener(CellEditorListener l) { - return false; } - @Override + @Override + public boolean shouldSelectCell(EventObject anEvent) + { + return false; + } + + @Override public boolean stopCellEditing() { - return false; + return false; } - } + } } diff --git a/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java b/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java new file mode 100644 index 00000000..e655c8d2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.gui; + +import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.gui.preference.PreferenceEditorViewRequest; +import io.github.dsheirer.gui.preference.PreferencesEditor; +import io.github.dsheirer.preference.UserPreferences; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Java FX window manager. Handles all secondary Java FX windows that are used within this primarily + * Swing application. + */ +public class JavaFxWindowManager +{ + private final static Logger mLog = LoggerFactory.getLogger(JavaFxWindowManager.class); + + private UserPreferences mUserPreferences; + private PreferencesEditor mPreferencesEditor; + + public JavaFxWindowManager(UserPreferences userPreferences) + { + mUserPreferences = userPreferences; + + //Register this class to receive events via each method annotated with @Subscribe + MyEventBus.getEventBus().register(this); + } + + /** + * Closes all JavaFX windows and shutsdown the FX thread + */ + public void shutdown() + { + Platform.exit(); + } + + @Subscribe + public void process(final PreferenceEditorViewRequest request) + { + if(mPreferencesEditor == null) + { + new JFXPanel(); + Platform.setImplicitExit(false); + Platform.runLater(() -> { + try + { + mPreferencesEditor = new PreferencesEditor(mUserPreferences); + + Stage stage = new Stage(); + stage.setOnHidden(event -> mPreferencesEditor = null); + mPreferencesEditor.start(stage); + mPreferencesEditor.showEditor(request); + } + catch(Throwable e) + { + mLog.error("Error launching user preferences window", e); + } + }); + } + else + { + Platform.runLater(() -> { + try + { + Stage stage = mPreferencesEditor.getStage(); + stage.show(); + mPreferencesEditor.showEditor(request); + } + catch(Throwable t) + { + mLog.error("Error showing existing preferences editor window", t); + } + }); + } + } +} diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index b996796a..2b732325 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,16 +15,16 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.gui; import com.jidesoft.swing.JideSplitPane; import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.alias.action.AliasActionManager; -import io.github.dsheirer.audio.AudioManager; +import io.github.dsheirer.audio.AudioPacketManager; import io.github.dsheirer.audio.broadcast.BroadcastModel; import io.github.dsheirer.audio.broadcast.BroadcastStatusPanel; +import io.github.dsheirer.audio.playback.AudioPlaybackManager; import io.github.dsheirer.controller.ControllerPanel; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.ChannelAutoStartFrame; @@ -31,10 +32,14 @@ import io.github.dsheirer.controller.channel.ChannelModel; import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.controller.channel.ChannelSelectionManager; import io.github.dsheirer.controller.channel.map.ChannelMapModel; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.gui.preference.PreferenceEditorType; +import io.github.dsheirer.gui.preference.PreferenceEditorViewRequest; import io.github.dsheirer.icon.IconManager; import io.github.dsheirer.map.MapService; import io.github.dsheirer.module.log.EventLogManager; import io.github.dsheirer.playlist.PlaylistManager; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.record.RecorderManager; import io.github.dsheirer.sample.Listener; @@ -87,6 +92,7 @@ public class SDRTrunk implements Listener private static final String PROPERTY_BROADCAST_STATUS_VISIBLE = "main.broadcast.status.visible"; private boolean mBroadcastStatusVisible; + private AudioPacketManager mAudioPacketManager; private IconManager mIconManager; private BroadcastStatusPanel mBroadcastStatusPanel; private BroadcastModel mBroadcastModel; @@ -98,6 +104,8 @@ public class SDRTrunk implements Listener private SpectralDisplayPanel mSpectralPanel; private JFrame mMainGui = new JFrame(); private JideSplitPane mSplitPane; + private JavaFxWindowManager mJavaFxWindowManager; + private UserPreferences mUserPreferences = new UserPreferences(); private String mTitle; @@ -108,10 +116,10 @@ public class SDRTrunk implements Listener mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***"); mLog.info("*******************************************************************"); mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]"); - mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors()); mLog.info("Host OS Name: " + System.getProperty("os.name")); mLog.info("Host OS Arch: " + System.getProperty("os.arch")); mLog.info("Host OS Version: " + System.getProperty("os.version")); + mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors()); mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory())); //Setup the application home directory @@ -145,36 +153,40 @@ public class SDRTrunk implements Listener EventLogManager eventLogManager = new EventLogManager(); - RecorderManager recorderManager = new RecorderManager(); + RecorderManager recorderManager = new RecorderManager(aliasModel); + + mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences); mSourceManager = new SourceManager(tunerModel, mSettingsManager); - mChannelProcessingManager = new ChannelProcessingManager(mChannelModel, channelMapModel, aliasModel, - eventLogManager, recorderManager, mSourceManager); - mChannelProcessingManager.addAudioPacketListener(recorderManager); + mChannelProcessingManager = new ChannelProcessingManager(channelMapModel, eventLogManager, recorderManager, + mSourceManager, aliasModel); mChannelModel.addListener(mChannelProcessingManager); + mChannelProcessingManager.addChannelEventListener(mChannelModel); - ChannelSelectionManager channelSelectionManager = - new ChannelSelectionManager(mChannelModel); + ChannelSelectionManager channelSelectionManager = new ChannelSelectionManager(mChannelModel); mChannelModel.addListener(channelSelectionManager); - AliasActionManager aliasActionManager = new AliasActionManager(); - mChannelProcessingManager.addMessageListener(aliasActionManager); + AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(mSourceManager.getMixerManager()); - AudioManager audioManager = new AudioManager(mSourceManager.getMixerManager()); - mChannelProcessingManager.addAudioPacketListener(audioManager); + mBroadcastModel = new BroadcastModel(aliasModel, mIconManager); - mBroadcastModel = new BroadcastModel(mIconManager); - - mChannelProcessingManager.addAudioPacketListener(mBroadcastModel); + //Audio packets are routed through the audio packet manager for metadata enrichment and then + //distributed to the audio packet processors (ie playback, recording, streaming, etc.) + mAudioPacketManager = new AudioPacketManager(aliasModel); + mAudioPacketManager.addListener(recorderManager); + mAudioPacketManager.addListener(audioPlaybackManager); + mAudioPacketManager.addListener(mBroadcastModel); + mAudioPacketManager.start(); + mChannelProcessingManager.addAudioPacketListener(mAudioPacketManager); MapService mapService = new MapService(mIconManager); - mChannelProcessingManager.addMessageListener(mapService); + mChannelProcessingManager.addDecodeEventListener(mapService); - mControllerPanel = new ControllerPanel(audioManager, aliasModel, mBroadcastModel, + mControllerPanel = new ControllerPanel(audioPlaybackManager, aliasModel, mBroadcastModel, mChannelModel, channelMapModel, mChannelProcessingManager, mIconManager, - mapService, mSettingsManager, mSourceManager, tunerModel); + mapService, mSettingsManager, mSourceManager, tunerModel, mUserPreferences); mSpectralPanel = new SpectralDisplayPanel(mChannelModel, mChannelProcessingManager, mSettingsManager, tunerModel); @@ -185,7 +197,7 @@ public class SDRTrunk implements Listener tunerModel.addListener(this); PlaylistManager playlistManager = new PlaylistManager(aliasModel, mBroadcastModel, mChannelModel, - channelMapModel); + channelMapModel, mUserPreferences); playlistManager.init(); @@ -338,6 +350,12 @@ public class SDRTrunk implements Listener viewMenu.add(new JSeparator()); viewMenu.add(new ClearTunerMenuItem(mSpectralPanel)); + viewMenu.add(new JSeparator()); + JMenuItem preferencesItem = new JMenuItem("Preferences"); + preferencesItem.addActionListener(e -> MyEventBus.getEventBus() + .post(new PreferenceEditorViewRequest(PreferenceEditorType.CHANNEL_EVENT))); + viewMenu.add(preferencesItem); + menuBar.add(viewMenu); JMenuItem screenCaptureItem = new JMenuItem("Screen Capture"); @@ -414,14 +432,16 @@ public class SDRTrunk implements Listener */ private void processShutdown() { - mLog.debug("Application shutdown started ..."); - mLog.debug("Stopping channels ..."); + mLog.info("Application shutdown started ..."); + mJavaFxWindowManager.shutdown(); + mLog.info("Stopping channels ..."); mChannelProcessingManager.shutdown(); - mLog.debug("Stopping spectral display ..."); + mAudioPacketManager.stop(); + mLog.info("Stopping spectral display ..."); mSpectralPanel.clearTuner(); - mLog.debug("Releasing tuners ..."); + mLog.info("Releasing tuners ..."); mSourceManager.shutdown(); - mLog.debug("Shutdown complete."); + mLog.info("Shutdown complete."); } /** diff --git a/src/main/java/io/github/dsheirer/gui/instrument/chart/IInstrumentedAFSK1200Decoder.java b/src/main/java/io/github/dsheirer/gui/instrument/chart/IInstrumentedAFSK1200Decoder.java index 446c6c64..5308cd81 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/chart/IInstrumentedAFSK1200Decoder.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/chart/IInstrumentedAFSK1200Decoder.java @@ -16,15 +16,15 @@ package io.github.dsheirer.gui.instrument.chart; import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.sample.Listener; import javafx.beans.property.SimpleIntegerProperty; public interface IInstrumentedAFSK1200Decoder { - public SimpleIntegerProperty getBufferCountProperty(); + SimpleIntegerProperty getBufferCountProperty(); - public AFSK1200DecoderInstrumented getAFSK1200Decoder(); + AFSK1200DecoderInstrumented getAFSK1200Decoder(); - public void setMessageListener(Listener listener); + void setMessageListener(Listener listener); } diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/AbstractAFSK1200Pane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/AbstractAFSK1200Pane.java index e4e48d1f..546b807d 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/AbstractAFSK1200Pane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/AbstractAFSK1200Pane.java @@ -20,7 +20,7 @@ import io.github.dsheirer.gui.instrument.chart.AFSK1200ZeroCrossingErrorDetector import io.github.dsheirer.gui.instrument.chart.DecodedSymbolChart; import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder; import io.github.dsheirer.gui.instrument.chart.RealSampleLineChart; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.ReusableFloatBuffer; @@ -52,7 +52,7 @@ public abstract class AbstractAFSK1200Pane extends RealDecoderPane private void init() { //This is force-cast without type checking .... - addListener((Listener)getDecoder()); + addListener((Listener) getDecoder()); addListener(getSampleLineChart()); @@ -69,12 +69,12 @@ public abstract class AbstractAFSK1200Pane extends RealDecoderPane getDecoder().getAFSK1200Decoder().addListener(getDecodedSymbolChart()); - getDecoder().setMessageListener(new Listener() + getDecoder().setMessageListener(new Listener() { @Override - public void receive(Message message) + public void receive(IMessage message) { - mLog.debug((message.isValid() ? "PASS " : "FAIL ") + message.getMessage()); + mLog.debug((message.isValid() ? "PASS " : "FAIL ") + message.toString()); } }); } @@ -144,7 +144,7 @@ public abstract class AbstractAFSK1200Pane extends RealDecoderPane if(mZeroCrossingErrorDetectorChart == null) { mZeroCrossingErrorDetectorChart = new AFSK1200ZeroCrossingErrorDetectorChart(getDecoder(), - getDecoder().getAFSK1200Decoder().getErrorDetector().getBuffer().length); + getDecoder().getAFSK1200Decoder().getErrorDetector().getBuffer().length); } return mZeroCrossingErrorDetectorChart; diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/LJ1200Pane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/LJ1200Pane.java index 41f6341c..84c1d94e 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/LJ1200Pane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/LJ1200Pane.java @@ -40,7 +40,7 @@ public class LJ1200Pane extends AbstractAFSK1200Pane mDecoder.getTowerMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mLog.debug("Tower Sync Detected!"); } @@ -54,7 +54,7 @@ public class LJ1200Pane extends AbstractAFSK1200Pane mDecoder.getTransponderMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mLog.debug("Transponder Sync Detected!"); } diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java index bf09bb33..ddefac8f 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java @@ -18,7 +18,7 @@ package io.github.dsheirer.gui.instrument.decoder; import io.github.dsheirer.gui.instrument.chart.LTRNetSampleBufferChart; import io.github.dsheirer.gui.instrument.chart.RealSampleLineChart; import io.github.dsheirer.gui.instrument.chart.ZeroCrossingErrorDetectorChart; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.MessageDirection; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.ltrnet.DecodeConfigLTRNet; @@ -72,13 +72,13 @@ public class LTRNetPane extends RealDecoderPane { DecodeConfigLTRNet config = new DecodeConfigLTRNet(); config.setMessageDirection(MessageDirection.OSW); - mDecoder = new LTRNetDecoderInstrumented(config, null); - mDecoder.setMessageListener(new Listener() + mDecoder = new LTRNetDecoderInstrumented(config); + mDecoder.setMessageListener(new Listener() { @Override - public void receive(Message message) + public void receive(IMessage message) { - mLog.debug(message.getMessage()); + mLog.debug(message.toString()); } }); } @@ -143,7 +143,7 @@ public class LTRNetPane extends RealDecoderPane if(mZeroCrossingErrorDetectorChart == null) { mZeroCrossingErrorDetectorChart = new ZeroCrossingErrorDetectorChart(getDecoder(), - getDecoder().getLTRDecoder().getErrorDetector().getBuffer().length); + getDecoder().getLTRDecoder().getErrorDetector().getBuffer().length); } return mZeroCrossingErrorDetectorChart; diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/MDC1200Pane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/MDC1200Pane.java index 431a86c0..81e63de2 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/MDC1200Pane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/MDC1200Pane.java @@ -40,7 +40,7 @@ public class MDC1200Pane extends AbstractAFSK1200Pane mDecoder.getMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mLog.debug("Sync Detected!"); } diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/MPT1327Pane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/MPT1327Pane.java index 5ddbf912..d0c910d9 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/MPT1327Pane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/MPT1327Pane.java @@ -40,7 +40,7 @@ public class MPT1327Pane extends AbstractAFSK1200Pane mDecoder.getControlMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { // mLog.debug("Control Sync Detected!"); } @@ -54,7 +54,7 @@ public class MPT1327Pane extends AbstractAFSK1200Pane mDecoder.getTrafficMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { // mLog.debug("Traffic Sync Detected!"); } diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1C4FMPane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1C4FMPane.java index 34755d18..d691c853 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1C4FMPane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1C4FMPane.java @@ -20,7 +20,7 @@ import io.github.dsheirer.gui.instrument.chart.DoubleLineChart; import io.github.dsheirer.gui.instrument.chart.EyeDiagramChart; import io.github.dsheirer.gui.instrument.chart.PhaseLineChart; import io.github.dsheirer.gui.instrument.chart.SymbolChart; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.p25.P25DecoderC4FMInstrumented; import io.github.dsheirer.sample.Listener; @@ -43,7 +43,7 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane private DoubleLineChart mPLLFrequencyLineChart; private DoubleLineChart mSamplesPerSymbolLineChart; private ReusableBufferBroadcaster mFilteredBufferBroadcaster = new ReusableBufferBroadcaster(); - private P25DecoderC4FMInstrumented mDecoder = new P25DecoderC4FMInstrumented(null); + private P25DecoderC4FMInstrumented mDecoder = new P25DecoderC4FMInstrumented(); public P25Phase1C4FMPane() { @@ -67,12 +67,12 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane HBox.setHgrow(getDecoderChartBox(), Priority.ALWAYS); getChildren().addAll(getSampleChartBox(), getDecoderChartBox()); - mDecoder.setMessageListener(new Listener() + mDecoder.setMessageListener(new Listener() { @Override - public void receive(Message message) + public void receive(IMessage message) { - mLog.debug(message.getMessage()); + mLog.debug(message.toString()); } }); @@ -86,7 +86,7 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane mDecoder.setSampleRate(sampleRate); double samplesPerSymbol = sampleRate / 4800.0; - getSampleLineChart().setSamplesPerSymbol((int)samplesPerSymbol); + getSampleLineChart().setSamplesPerSymbol((int) samplesPerSymbol); } private P25DecoderC4FMInstrumented getDecoder() @@ -117,7 +117,7 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane HBox.setHgrow(getPLLPhaseErrorLineChart(), Priority.ALWAYS); HBox.setHgrow(getPLLFrequencyLineChart(), Priority.ALWAYS); mDecoderChartBox.getChildren().addAll(getSymbolChart(), getPLLPhaseErrorLineChart(), - getPLLFrequencyLineChart()); + getPLLFrequencyLineChart()); } return mDecoderChartBox; @@ -136,7 +136,7 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane HBox.setHgrow(getEyeDiagramChart(), Priority.ALWAYS); HBox.setHgrow(getSamplesPerSymbolLineChart(), Priority.ALWAYS); mSampleChartBox.getChildren().addAll(getSampleLineChart(), getEyeDiagramChart(), - getSamplesPerSymbolLineChart()); + getSamplesPerSymbolLineChart()); } return mSampleChartBox; @@ -176,7 +176,7 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane { if(mPLLFrequencyLineChart == null) { - mPLLFrequencyLineChart = new DoubleLineChart( "PLL Frequency", -500, 500, 50, 40); + mPLLFrequencyLineChart = new DoubleLineChart("PLL Frequency", -500, 500, 50, 40); } return mPLLFrequencyLineChart; @@ -186,7 +186,7 @@ public class P25Phase1C4FMPane extends ComplexDecoderPane { if(mSamplesPerSymbolLineChart == null) { - mSamplesPerSymbolLineChart = new DoubleLineChart( "Sample Point", 9.5, 11.5, 0.1, 40); + mSamplesPerSymbolLineChart = new DoubleLineChart("Sample Point", 9.5, 11.5, 0.1, 40); } return mSamplesPerSymbolLineChart; diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1LSMPane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1LSMPane.java index d80bf4e7..3272889f 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1LSMPane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/P25Phase1LSMPane.java @@ -21,7 +21,7 @@ import io.github.dsheirer.gui.instrument.chart.EyeDiagramChart; import io.github.dsheirer.gui.instrument.chart.PhaseLineChart; import io.github.dsheirer.gui.instrument.chart.SamplesPerSymbolChart; import io.github.dsheirer.gui.instrument.chart.SymbolChart; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.p25.P25DecoderLSMInstrumented; import io.github.dsheirer.sample.Listener; @@ -44,7 +44,7 @@ public class P25Phase1LSMPane extends ComplexDecoderPane private DoubleLineChart mPLLFrequencyLineChart; private SamplesPerSymbolChart mSamplesPerSymbolLineChart; private ReusableBufferBroadcaster mFilteredBufferBroadcaster = new ReusableBufferBroadcaster(); - private P25DecoderLSMInstrumented mDecoder = new P25DecoderLSMInstrumented(null); + private P25DecoderLSMInstrumented mDecoder = new P25DecoderLSMInstrumented(); public P25Phase1LSMPane() { @@ -68,12 +68,12 @@ public class P25Phase1LSMPane extends ComplexDecoderPane HBox.setHgrow(getBottomChartBox(), Priority.ALWAYS); getChildren().addAll(getTopChartBox(), getBottomChartBox()); - mDecoder.setMessageListener(new Listener() + mDecoder.setMessageListener(new Listener() { @Override - public void receive(Message message) + public void receive(IMessage message) { - mLog.debug(message.getMessage()); + mLog.debug(message.toString()); } }); @@ -87,7 +87,7 @@ public class P25Phase1LSMPane extends ComplexDecoderPane mDecoder.setSampleRate(sampleRate); double samplesPerSymbol = sampleRate / 4800.0; - getDifferentialDemodulatedSamplesChartBox().setSamplesPerSymbol((int)samplesPerSymbol); + getDifferentialDemodulatedSamplesChartBox().setSamplesPerSymbol((int) samplesPerSymbol); getSamplesPerSymbolLineChart().setSamplesPerSymbol(samplesPerSymbol); } @@ -119,7 +119,7 @@ public class P25Phase1LSMPane extends ComplexDecoderPane HBox.setHgrow(getPLLPhaseErrorLineChart(), Priority.ALWAYS); HBox.setHgrow(getPLLFrequencyLineChart(), Priority.ALWAYS); mBottomChartBox.getChildren().addAll(getSymbolConstellationChart(), getPLLPhaseErrorLineChart(), - getPLLFrequencyLineChart()); + getPLLFrequencyLineChart()); } return mBottomChartBox; @@ -138,7 +138,7 @@ public class P25Phase1LSMPane extends ComplexDecoderPane HBox.setHgrow(getEyeDiagramChart(), Priority.ALWAYS); HBox.setHgrow(getSamplesPerSymbolLineChart(), Priority.ALWAYS); mTopChartBox.getChildren().addAll(getDifferentialDemodulatedSamplesChartBox(), getEyeDiagramChart(), - getSamplesPerSymbolLineChart()); + getSamplesPerSymbolLineChart()); } return mTopChartBox; @@ -178,7 +178,7 @@ public class P25Phase1LSMPane extends ComplexDecoderPane { if(mPLLFrequencyLineChart == null) { - mPLLFrequencyLineChart = new DoubleLineChart( "PLL Frequency", -2500, 2500, 200, 40); + mPLLFrequencyLineChart = new DoubleLineChart("PLL Frequency", -2500, 2500, 200, 40); } return mPLLFrequencyLineChart; diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/Tait1200Pane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/Tait1200Pane.java index feee0a58..c3b23aaa 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/Tait1200Pane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/Tait1200Pane.java @@ -40,7 +40,7 @@ public class Tait1200Pane extends AbstractAFSK1200Pane mDecoder.getANIMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mLog.debug("ANI Sync Detected!"); } @@ -54,7 +54,7 @@ public class Tait1200Pane extends AbstractAFSK1200Pane mDecoder.getGPSMessageFramer().setSyncDetectListener(new ISyncDetectListener() { @Override - public void syncDetected() + public void syncDetected(int bitErrors) { mLog.debug("GPS Sync Detected!"); } diff --git a/src/main/java/io/github/dsheirer/gui/preference/DecodeEventViewPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/DecodeEventViewPreferenceEditor.java new file mode 100644 index 00000000..f5e34074 --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/DecodeEventViewPreferenceEditor.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.gui.preference; + +import io.github.dsheirer.preference.TimestampFormat; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.event.DecodeEventPreference; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Label; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; + +import java.util.Date; + + +/** + * Preference settings for channel event view + */ +public class DecodeEventViewPreferenceEditor extends HBox +{ + private DecodeEventPreference mDecodeEventPreference; + private GridPane mEditorPane; + private ChoiceBox mTimestampFormatChoiceBox; + private Label mTimestampFormatLabel; + + public DecodeEventViewPreferenceEditor(UserPreferences userPreferences) + { + mDecodeEventPreference = userPreferences.getDecodeEventPreference(); + getChildren().add(getEditorPane()); + } + + private GridPane getEditorPane() + { + if(mEditorPane == null) + { + mEditorPane = new GridPane(); + mEditorPane.setPadding(new Insets(10, 10, 10, 10)); + GridPane.setMargin(getTimestampFormatLabel(), new Insets(0, 10, 0, 0)); + GridPane.setHalignment(getTimestampFormatLabel(), HPos.LEFT); + mEditorPane.add(getTimestampFormatLabel(), 0, 0); + mEditorPane.add(getTimestampFormatChoiceBox(), 1, 0); + } + + return mEditorPane; + } + + private Label getTimestampFormatLabel() + { + if(mTimestampFormatLabel == null) + { + mTimestampFormatLabel = new Label("Timestamp Format"); + } + + return mTimestampFormatLabel; + } + + private ChoiceBox getTimestampFormatChoiceBox() + { + if(mTimestampFormatChoiceBox == null) + { + mTimestampFormatChoiceBox = new ChoiceBox<>(); + + for(TimestampFormat format: TimestampFormat.values()) + { + mTimestampFormatChoiceBox.getItems().add(new DisplayableTimestamp(format)); + } + + TimestampFormat current = mDecodeEventPreference.getTimestampFormat(); + + for(DisplayableTimestamp displayableTimestamp: mTimestampFormatChoiceBox.getItems()) + { + if(displayableTimestamp.getTimestampFormat() == current) + { + mTimestampFormatChoiceBox.getSelectionModel().select(displayableTimestamp); + continue; + } + } + + mTimestampFormatChoiceBox.setOnAction(event -> { + DisplayableTimestamp selected = mTimestampFormatChoiceBox.getSelectionModel().getSelectedItem(); + mDecodeEventPreference.setTimestampFormat(selected.getTimestampFormat()); + }); + } + + return mTimestampFormatChoiceBox; + } + + public class DisplayableTimestamp + { + private TimestampFormat mTimestampFormat; + + public DisplayableTimestamp(TimestampFormat timestampFormat) + { + mTimestampFormat = timestampFormat; + } + + public TimestampFormat getTimestampFormat() + { + return mTimestampFormat; + } + + public String toString() + { + return mTimestampFormat.getFormatter().format(new Date(System.currentTimeMillis())); + } + } +} diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java new file mode 100644 index 00000000..831cbc28 --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.gui.preference; + +import io.github.dsheirer.preference.UserPreferences; +import javafx.scene.Node; + +/** + * Creates an editor for the specified preference editor type + */ +public class PreferenceEditorFactory +{ + public static Node getEditor(PreferenceEditorType preferenceEditorType, UserPreferences userPreferences) + { + switch(preferenceEditorType) + { + case TALKGROUP_FORMAT: + return new TalkgroupFormatPreferenceEditor(userPreferences); + case CHANNEL_EVENT: + return new DecodeEventViewPreferenceEditor(userPreferences); + } + + return null; + } +} diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java new file mode 100644 index 00000000..c12c8796 --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.gui.preference; + +/** + * Preference editor tree node enumeration. + */ +public enum PreferenceEditorType +{ + CHANNEL_EVENT("Channel Events"), + TALKGROUP_FORMAT("Talkgroups"); + + private String mLabel; + + PreferenceEditorType(String label) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } +} diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorViewRequest.java b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorViewRequest.java new file mode 100644 index 00000000..2edee61a --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorViewRequest.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.gui.preference; + +/** + * Request to launch the preferences editor and scroll to a specific editor view + */ +public class PreferenceEditorViewRequest +{ + private PreferenceEditorType mPreferenceType; + + public PreferenceEditorViewRequest(PreferenceEditorType preferenceEditorType) + { + mPreferenceType = preferenceEditorType; + } + + public PreferenceEditorType getPreferenceType() + { + return mPreferenceType; + } +} diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java b/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java new file mode 100644 index 00000000..617a4031 --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java @@ -0,0 +1,291 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.gui.preference; + +import io.github.dsheirer.preference.UserPreferences; +import javafx.application.Application; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import javafx.stage.Window; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.ListIterator; +import java.util.Map; + +/** + * Preferences editor dialog + */ +public class PreferencesEditor extends Application +{ + private final static Logger mLog = LoggerFactory.getLogger(PreferencesEditor.class); + + private UserPreferences mUserPreferences; + private BorderPane mBorderPane; + private TreeView mEditorSelectionTreeView; + private DecodeEventViewPreferenceEditor mDecodeEventViewPreferenceEditor; + private VBox mDefaultEditor; + private HBox mControlBox; + + /** + * Constructs a preferences editor instance + */ + public PreferencesEditor() + { + } + + public Stage getStage() + { + try + { + Window window = getBorderPane().getScene().getWindow(); + + if(window instanceof Stage) + { + return (Stage)window; + } + } + catch(Throwable t) + { + mLog.debug("Error", t); + } + + return null; + } + + public PreferencesEditor(UserPreferences userPreferences) + { + mUserPreferences = userPreferences; + } + + private UserPreferences getUserPreferences() + { + if(mUserPreferences == null) + { + mUserPreferences = new UserPreferences(); + } + + return mUserPreferences; + } + + @Override + public void start(Stage stage) throws Exception + { + stage.setTitle("Preferences"); + Scene scene = new Scene(getBorderPane(), 800, 500); + stage.setScene(scene); + stage.show(); + } + + /** + * Shows the editor specified in the request by scrolling the editor view tree to the selected item. + */ + public void showEditor(PreferenceEditorViewRequest request) + { + TreeItem toSelect = recursivelyFindEditorType(getEditorSelectionTreeView().getRoot(), request.getPreferenceType()); + + if(toSelect != null) + { + getEditorSelectionTreeView().getSelectionModel().select(toSelect); + } + } + + private TreeItem recursivelyFindEditorType(TreeItem parent, PreferenceEditorType type) + { + ListIterator li = parent.getChildren().listIterator(); + + while(li.hasNext()) + { + TreeItem treeItem = li.next(); + + if(treeItem.getValue() instanceof PreferenceEditorType && + ((PreferenceEditorType)treeItem.getValue()).equals(type)) + { + return treeItem; + } + else + { + TreeItem item = recursivelyFindEditorType(treeItem, type); + + if(item != null) + { + return item; + } + } + } + + return null; + } + + + /** + * Primary layout for the editor window + */ + private BorderPane getBorderPane() + { + if(mBorderPane == null) + { + mBorderPane = new BorderPane(); + mBorderPane.setLeft(getEditorSelectionTreeView()); + mBorderPane.setCenter(getDefaultEditor()); + mBorderPane.setBottom(getControlBox()); + } + + return mBorderPane; + } + + private VBox getDefaultEditor() + { + if(mDefaultEditor == null) + { + mDefaultEditor = new VBox(); + mDefaultEditor.setPadding(new Insets(10, 10, 10, 10)); + Label label = new Label("Please select a preference ..."); + + mDefaultEditor.getChildren().add(label); + } + + return mDefaultEditor; + } + + /** + * Preference type selection list + */ + private TreeView getEditorSelectionTreeView() + { + if(mEditorSelectionTreeView == null) + { + TreeItem treeRoot = new TreeItem<>("Root node"); + + TreeItem displayItem = new TreeItem<>("Display"); + displayItem.getChildren().add(new TreeItem(PreferenceEditorType.CHANNEL_EVENT)); + displayItem.getChildren().add(new TreeItem(PreferenceEditorType.TALKGROUP_FORMAT)); + treeRoot.getChildren().add(displayItem); + displayItem.setExpanded(true); + + mEditorSelectionTreeView = new TreeView(); + mEditorSelectionTreeView.setShowRoot(false); + mEditorSelectionTreeView.setRoot(treeRoot); + treeRoot.setExpanded(true); + mEditorSelectionTreeView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); + mEditorSelectionTreeView.getSelectionModel().selectedItemProperty().addListener(new EditorTreeSelectionListener()); + } + + return mEditorSelectionTreeView; + } + + /** + * Decode event view preferences editor + */ + private DecodeEventViewPreferenceEditor getDecodeEventViewPreferenceEditor() + { + if(mDecodeEventViewPreferenceEditor == null) + { + mDecodeEventViewPreferenceEditor = new DecodeEventViewPreferenceEditor(getUserPreferences()); + } + + return mDecodeEventViewPreferenceEditor; + } + + /** + * Control box with OK button. + */ + private HBox getControlBox() + { + if(mControlBox == null) + { + mControlBox = new HBox(); + Button okButton = new Button("Ok"); + okButton.setOnAction(event -> { + Stage stage = (Stage)getBorderPane().getScene().getWindow(); + stage.close(); + }); + HBox.setMargin(okButton, new Insets(5, 5, 5, 5)); + mControlBox.setAlignment(Pos.CENTER_RIGHT); + mControlBox.getChildren().add(okButton); + } + + return mControlBox; + } + + /** + * Listens for editor tree selection events and creates a preference editor instance for each type as needed. + * + * Constructed editors are cached for reuse. + */ + public class EditorTreeSelectionListener implements ChangeListener + { + private Map mEditors = new HashMap<>(); + + @Override + public void changed(ObservableValue observable, Object oldValue, Object newValue) + { + Node editor = getEditor(newValue); + BorderPane.setAlignment(editor, Pos.CENTER_LEFT); + getBorderPane().setCenter(editor); + } + + private Node getEditor(Object treeNodeItem) + { + if(treeNodeItem instanceof TreeItem) + { + Object value = ((TreeItem)treeNodeItem).getValue(); + + if(value instanceof PreferenceEditorType) + { + PreferenceEditorType type = (PreferenceEditorType)value; + + Node editor = mEditors.get(type); + + if(editor != null) + { + return editor; + } + + editor = PreferenceEditorFactory.getEditor(type, getUserPreferences()); + + if(editor != null) + { + mEditors.put(type, editor); + return editor; + } + } + } + + return getDefaultEditor(); + } + } + + public static void main(String[] args) + { + launch(args); + } +} diff --git a/src/main/java/io/github/dsheirer/gui/preference/TalkgroupFormatPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/TalkgroupFormatPreferenceEditor.java new file mode 100644 index 00000000..12893b56 --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/TalkgroupFormatPreferenceEditor.java @@ -0,0 +1,120 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.gui.preference; + +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.identifier.IntegerFormat; +import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference; +import io.github.dsheirer.protocol.Protocol; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Label; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; + + +/** + * Preference settings for channel event view + */ +public class TalkgroupFormatPreferenceEditor extends HBox +{ + private TalkgroupFormatPreference mTalkgroupFormatPreference; + private GridPane mEditorPane; + + public TalkgroupFormatPreferenceEditor(UserPreferences userPreferences) + { + mTalkgroupFormatPreference = userPreferences.getTalkgroupFormatPreference(); + getChildren().add(getEditorPane()); + } + + private GridPane getEditorPane() + { + if(mEditorPane == null) + { + mEditorPane = new GridPane(); + mEditorPane.setPadding(new Insets(10, 10, 10, 10)); + + int row = 0; + + mEditorPane.add(new Label("Protocol"), 0, row); + mEditorPane.add(new Label("Display Format"), 1, row++); + + for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS) + { + Label label = new Label(protocol.toString()); + GridPane.setMargin(label, new Insets(0, 10, 0, 0)); + GridPane.setHalignment(label, HPos.LEFT); + mEditorPane.add(label, 0, row); + IntegerFormatEditor editor = new IntegerFormatEditor(mTalkgroupFormatPreference, protocol); + GridPane.setMargin(editor, new Insets(5,5,5,5)); + mEditorPane.add(editor, 1, row); + FixedWidthEditor fixedWidthEditor = new FixedWidthEditor(mTalkgroupFormatPreference, protocol); + GridPane.setMargin(fixedWidthEditor, new Insets(0,0,0,10)); + mEditorPane.add(fixedWidthEditor, 2, row); + row++; + } + } + + return mEditorPane; + } + + /** + * Choice box control for tracking talkgroup format preference per protocol + */ + public class IntegerFormatEditor extends ChoiceBox + { + private TalkgroupFormatPreference mTalkgroupFormatPreference; + private Protocol mProtocol; + + public IntegerFormatEditor(TalkgroupFormatPreference preference, Protocol protocol) + { + mTalkgroupFormatPreference = preference; + mProtocol = protocol; + getItems().addAll(IntegerFormat.values()); + getSelectionModel().select(mTalkgroupFormatPreference.getTalkgroupFormat(mProtocol)); + + setOnAction(event -> { + IntegerFormat selected = getSelectionModel().getSelectedItem(); + mTalkgroupFormatPreference.setTalkgroupFormat(mProtocol, selected); + }); + } + } + + /** + * Check box control for tracking fixed width preference per protocol + */ + public class FixedWidthEditor extends CheckBox + { + private TalkgroupFormatPreference mTalkgroupFormatPreference; + private Protocol mProtocol; + + public FixedWidthEditor(TalkgroupFormatPreference preference, Protocol protocol) + { + super("Fixed Width"); + mTalkgroupFormatPreference = preference; + mProtocol = protocol; + setSelected(mTalkgroupFormatPreference.isTalkgroupFixedWidth(mProtocol)); + setOnAction(event -> mTalkgroupFormatPreference.setTalkgroupFixedWidth(mProtocol, isSelected())); + } + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/Form.java b/src/main/java/io/github/dsheirer/identifier/Form.java new file mode 100644 index 00000000..09f7ddf6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/Form.java @@ -0,0 +1,54 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.identifier; + +/** + * Identifier form. Indicates the type of identifier. + */ +public enum Form +{ + ALIAS_LIST, + CHANNEL, + CHANNEL_DESCRIPTOR, + CHANNEL_NAME, + CHANNEL_FREQUENCY, + DECODER_TYPE, + ENCRYPTION_KEY, + ESN, + IPV4_ADDRESS, + LOCATION_REGISTRATION_AREA, + LOJACK, + NEIGHBOR_SITE, + NETWORK_ACCESS_CODE, + PATCH_GROUP, + RF_SUBSYSTEM, + SHORT_DATA_MESSAGE, + SITE, + STATE, + SYSTEM, + TALKGROUP, + TELEPHONE_NUMBER, + UNIT_IDENTIFIER, + UNIT_STATUS, + USER_STATUS, + UNIQUE_ID, + WACN, + ANY; +} diff --git a/src/main/java/io/github/dsheirer/identifier/Identifier.java b/src/main/java/io/github/dsheirer/identifier/Identifier.java new file mode 100644 index 00000000..928bb349 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/Identifier.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.identifier; + +import io.github.dsheirer.protocol.Protocol; + +import java.util.Objects; + +/** + * Identifier base class. + * + * Note: Identifiers are intended to be immutable. + */ +public abstract class Identifier +{ + private T mValue; + private IdentifierClass mIdentifierClass; + private Form mForm; + private Role mRole; + + public Identifier(T value, IdentifierClass identifierClass, Form form, Role role) + { + mValue = value; + mIdentifierClass = identifierClass; + mForm = form; + mRole = role; + } + + public String debug() + { + StringBuilder sb = new StringBuilder(); + sb.append("CLASS:").append(getIdentifierClass()); + sb.append(" FORM:").append(getForm()); + sb.append(" VALUE:").append(getValue()); + sb.append(" VALUE CLASS:").append(getValue().getClass()); + sb.append(" CLASS:").append(getClass()); + return sb.toString(); + } + + /** + * Validity test for the identifier. This method always returns true unless a subclass + * implementation overrides the value for a specific validity test implementation. + */ + public boolean isValid() + { + return true; + } + + /** + * Value of the identifier + */ + public T getValue() + { + return mValue; + } + + /** + * Class/type of identifier + */ + public IdentifierClass getIdentifierClass() + { + return mIdentifierClass; + } + + public Form getForm() + { + return mForm; + } + + /** + * Role of the identifier in the communications + */ + public Role getRole() + { + return mRole; + } + + /** + * Protocol that produces this identifier + */ + public abstract Protocol getProtocol(); + + @Override + public String toString() + { + return getValue().toString(); + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + Identifier that = (Identifier)o; + return Objects.equals(getValue(), that.getValue()) && + getIdentifierClass() == that.getIdentifierClass() && + getForm() == that.getForm() && + getRole() == that.getRole(); + } + + @Override + public int hashCode() + { + return Objects.hash(getValue(), getIdentifierClass(), getForm(), getRole()); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java b/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java new file mode 100644 index 00000000..c0ff2bc2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.identifier; + +/** + * Identifier form. Indicates the type of identifier. + */ +public enum IdentifierClass +{ + //Decoder configuration detail + CONFIGURATION, + + //Decoder state or information + DECODER, + + //Communication network identifier + NETWORK, + + //Communication user identifier + USER; +} diff --git a/src/main/java/io/github/dsheirer/identifier/IdentifierCollection.java b/src/main/java/io/github/dsheirer/identifier/IdentifierCollection.java new file mode 100644 index 00000000..a2309804 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/IdentifierCollection.java @@ -0,0 +1,296 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier; + +import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * (Immutable) Collection of identifiers with convenient accessor methods + * + * @see MutableIdentifierCollection for the mutable version of this class + */ +public class IdentifierCollection +{ + private final static Logger mLog = LoggerFactory.getLogger(IdentifierCollection.class); + protected List mIdentifiers = new ArrayList<>(); + protected AliasListConfigurationIdentifier mAliasListConfigurationIdentifier; + private boolean mUpdated = false; + + /** + * Constructs an empty identifier collection + */ + public IdentifierCollection() + { + } + + public IdentifierCollection(Collection identifiers) + { + for(Identifier identifier: identifiers) + { + if(identifier == null) + { + throw new IllegalArgumentException("Identifier cannot be null"); + } + + mIdentifiers.add(identifier); + + if(identifier instanceof AliasListConfigurationIdentifier) + { + mAliasListConfigurationIdentifier = (AliasListConfigurationIdentifier)identifier; + } + } + } + + public IdentifierCollection(Identifier identifier) + { + if(identifier == null) + { + throw new IllegalArgumentException("Identifier cannot be null"); + } + + mIdentifiers.add(identifier); + } + + /** + * Indicates if this identifier collection contains an updated list of identifiers. + */ + public boolean isUpdated() + { + return mUpdated; + } + + protected void setUpdated(boolean updated) + { + mUpdated = updated; + } + + /** + * Alias List configuration identifier containing the name of the alias list for this collection. + * @return alias list or null + */ + public AliasListConfigurationIdentifier getAliasListConfiguration() + { + return mAliasListConfigurationIdentifier; + } + + /** + * Indicates if this collection has an alias list specified. + */ + public boolean hasAliasListConfiguration() + { + return mAliasListConfigurationIdentifier != null; + } + + /** + * Immutable list of identifiers contained in this collection + */ + public List getIdentifiers() + { + return Collections.unmodifiableList(mIdentifiers); + } + + /** + * Get a list of identifiers by identifier class from this collection. + * + * @param identifierClass to match + * @return list of zero or more identifiers + */ + public List getIdentifiers(IdentifierClass identifierClass) + { + List identifiers = new ArrayList<>(); + + for(Identifier identifier : mIdentifiers) + { + if(identifier.getIdentifierClass() == identifierClass) + { + identifiers.add(identifier); + } + } + + return identifiers; + } + + /** + * Get a list of identifiers by form from this collection. + * + * @param form to match + * @return list of zero or more identifiers + */ + public List getIdentifiers(Form form) + { + List identifiers = new ArrayList<>(); + + for(Identifier identifier : mIdentifiers) + { + if(identifier.getForm() == form) + { + identifiers.add(identifier); + } + } + + return identifiers; + } + + /** + * Get a list of identifiers by role from this collection. + * + * @param role to match + * @return list of zero or more identifiers + */ + public List getIdentifiers(Role role) + { + List identifiers = new ArrayList<>(); + + for(Identifier identifier : mIdentifiers) + { + try + { + if(identifier.getRole() == role) + { + identifiers.add(identifier); + } + } + catch(NullPointerException npe) + { + StringBuilder sb = new StringBuilder(); + for(Identifier i : mIdentifiers) + { + if(i == null) + { + sb.append("Identifier: (null)").append("\n"); + } + else + { + sb.append("Identifier: ").append(i).append(" Class:").append(i.getClass()).append("\n"); + } + } + mLog.warn("An identifier in a collection is somehow null ...\n" + sb.toString()); + } + } + + return identifiers; + } + + /** + * Get a list of identifiers by identifier class and role from this collection. + * + * @param identifierClass to match + * @param role to match + * @return list of zero or more identifiers + */ + public List getIdentifiers(IdentifierClass identifierClass, Role role) + { + List identifiers = new ArrayList<>(); + + for(Identifier identifier : mIdentifiers) + { + if(identifier.getIdentifierClass() == identifierClass && identifier.getRole() == role) + { + identifiers.add(identifier); + } + } + + return identifiers; + } + + /** + * Get a list of identifiers by identifier class and form from this collection. + * + * @param identifierClass to match + * @param form to match + * @return list of zero or more identifiers + */ + public List getIdentifiers(IdentifierClass identifierClass, Form form) + { + List identifiers = new ArrayList<>(); + + for(Identifier identifier : mIdentifiers) + { + if(identifier.getIdentifierClass() == identifierClass && identifier.getForm() == form) + { + identifiers.add(identifier); + } + } + + return identifiers; + } + + /** + * Get the single identifier by identifier class, form and role from this collection. + * + * @param identifierClass to match + * @param form to match + * @param role to match + * @return matching identifier or null + */ + public Identifier getIdentifier(IdentifierClass identifierClass, Form form, Role role) + { + for(Identifier identifier : mIdentifiers) + { + if(identifier.getIdentifierClass() == identifierClass && + identifier.getForm() == form && + identifier.getRole() == role) + { + return identifier; + } + } + + return null; + } + + /** + * Returns the first identifier in this collection that is assigned a FROM role + */ + public Identifier getFromIdentifier() + { + List fromIdentifiers = getIdentifiers(Role.FROM); + + if(!fromIdentifiers.isEmpty()) + { + return fromIdentifiers.get(0); + } + + return null; + } + + /** + * Returns the first identifier in this collection that is assigned a FROM role + */ + public Identifier getToIdentifier() + { + List toIdentifiers = getIdentifiers(Role.TO); + + if(!toIdentifiers.isEmpty()) + { + return toIdentifiers.get(0); + } + + return null; + } + +} diff --git a/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateListener.java b/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateListener.java new file mode 100644 index 00000000..6c261b6d --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateListener.java @@ -0,0 +1,30 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.identifier; + +import io.github.dsheirer.sample.Listener; + +/** + * Interface for receiving identifier update notifications + */ +public interface IdentifierUpdateListener +{ + Listener getIdentifierUpdateListener(); +} diff --git a/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateNotification.java b/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateNotification.java new file mode 100644 index 00000000..a948bfb2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateNotification.java @@ -0,0 +1,87 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier; + +/** + * Request to add or update an identifier + */ +public class IdentifierUpdateNotification +{ + private Identifier mIdentifier; + private Operation mOperation; + + /** + * Constructs an identifier update notification + * @param identifier that has been updated + */ + public IdentifierUpdateNotification(Identifier identifier, Operation operation) + { + mIdentifier = identifier; + mOperation = operation; + } + + public Identifier getIdentifier() + { + return mIdentifier; + } + + public Operation getOperation() + { + return mOperation; + } + + public boolean isAdded() + { + return mOperation == Operation.ADD; + } + + public boolean isRemoved() + { + return mOperation == Operation.REMOVE; + } + + /** + * Indicates that this update should not be rebroadcast by any of the consumers. + */ + public boolean isSilentAdd() + { + return mOperation == Operation.SILENT_ADD; + } + + /** + * Indicates that this update should not be rebroadcast by any of the consumers. + */ + public boolean isSilentRemove() + { + return mOperation == Operation.SILENT_REMOVE; + } + + @Override + public String toString() + { + return mOperation.name() + " - " + mIdentifier; + } + + public enum Operation + { + ADD, REMOVE, SILENT_ADD, SILENT_REMOVE; + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateProvider.java b/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateProvider.java new file mode 100644 index 00000000..ce6a7201 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/IdentifierUpdateProvider.java @@ -0,0 +1,29 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.identifier; + +import io.github.dsheirer.sample.Listener; + +public interface IdentifierUpdateProvider +{ + void setIdentifierUpdateListener(Listener listener); + + void removeIdentifierUpdateListener(); +} diff --git a/src/main/java/io/github/dsheirer/identifier/MutableIdentifierCollection.java b/src/main/java/io/github/dsheirer/identifier/MutableIdentifierCollection.java new file mode 100644 index 00000000..c18e6cb7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/MutableIdentifierCollection.java @@ -0,0 +1,399 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier; + +import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; +import io.github.dsheirer.sample.Listener; + +import java.util.Collection; +import java.util.Iterator; + +/** + * Identifier collection with methods for changing or updating managed identifiers + */ +public class MutableIdentifierCollection extends IdentifierCollection implements IdentifierUpdateProvider, + Listener +{ + private Listener mListener; + + public MutableIdentifierCollection() + { + } + + public MutableIdentifierCollection(Collection identifiers) + { + for(Identifier identifier: identifiers) + { + update(identifier); + } + } + + /** + * Broadcasts all of the currently held identifiers to the registered listener + */ + public void broadcastIdentifiers() + { + for(Identifier identifier : getIdentifiers()) + { + notifyAdd(identifier); + } + } + + /** + * Registers a listener to be notified when identifiers are added to or removed from this collection + */ + @Override + public void setIdentifierUpdateListener(Listener listener) + { + mListener = listener; + } + + /** + * Unregisters the listener from receiving identifier update notifications + */ + @Override + public void removeIdentifierUpdateListener() + { + mListener = null; + } + + /** + * Notifies a registered listener that the identifier has been added to this collection + */ + private void notifyAdd(Identifier identifier) + { + setUpdated(true); + if(mListener != null) + { + mListener.receive(new IdentifierUpdateNotification(identifier, IdentifierUpdateNotification.Operation.ADD)); + } + } + + /** + * Notifies a registered listener that the identifier has been removed from this collection + */ + private void notifyRemove(Identifier identifier) + { + setUpdated(true); + if(mListener != null) + { + mListener.receive(new IdentifierUpdateNotification(identifier, + IdentifierUpdateNotification.Operation.REMOVE)); + } + } + + /** + * Adds the identifier to this collection if not already contained in this collection. + * + * @param identifier to add + */ + private void add(Identifier identifier) + { + if(identifier.isValid() && !mIdentifiers.contains(identifier)) + { + mIdentifiers.add(identifier); + notifyAdd(identifier); + } + + //Retain a reference to the alias list identifier separately so that it can be accessed quickly. + if(identifier instanceof AliasListConfigurationIdentifier) + { + mAliasListConfigurationIdentifier = (AliasListConfigurationIdentifier)identifier; + } + } + + /** + * Adds the identifier to this collection if not already contained in this collection. Does NOT broadcast an + * update notification. + * + * @param identifier to add + */ + private void silentAdd(Identifier identifier) + { + if(identifier.isValid() && !mIdentifiers.contains(identifier)) + { + mIdentifiers.add(identifier); + } + + //Retain a reference to the alias list identifier separately so that it can be accessed quickly. + if(identifier instanceof AliasListConfigurationIdentifier) + { + mAliasListConfigurationIdentifier = (AliasListConfigurationIdentifier)identifier; + } + } + + /** + * Removes the identifier from this collection + */ + public void remove(Identifier identifier) + { + if(mIdentifiers.remove(identifier)) + { + notifyRemove(identifier); + } + + //Remove the reference to the alias list identifier. + if(identifier instanceof AliasListConfigurationIdentifier) + { + mAliasListConfigurationIdentifier = null; + } + } + + /** + * Removes the identifier from this collection and does NOT broadcast an update notification + */ + public void silentRemove(Identifier identifier) + { + mIdentifiers.remove(identifier); + + //Remove the reference to the alias list identifier. + if(identifier instanceof AliasListConfigurationIdentifier) + { + mAliasListConfigurationIdentifier = null; + } + } + + /** + * Adds the identifier only if the value is different from the existing identifiers(s). If + * there are multiple matching identifiers, then they are all removed and replaced by this + * single identifier. + * + * @param identifier + */ + public void update(Identifier identifier) + { + if(identifier != null) + { + Identifier existing = getIdentifier(identifier.getIdentifierClass(), + identifier.getForm(), identifier.getRole()); + + if(existing != null) + { + if(!existing.equals(identifier)) + { + remove(existing); + add(identifier); + } + } + else + { + add(identifier); + } + } + } + + /** + * Adds the identifier only if the value is different from the existing identifiers(s). If + * there are multiple matching identifiers, then they are all removed and replaced by this + * single identifier. + * + * @param identifier + */ + public void silentUpdate(Identifier identifier) + { + if(identifier != null) + { + Identifier existing = getIdentifier(identifier.getIdentifierClass(), identifier.getForm(), identifier.getRole()); + + if(existing != null) + { + if(!existing.equals(identifier)) + { + silentRemove(existing); + silentAdd(identifier); + } + } + else + { + silentAdd(identifier); + } + } + } + + /** + * Updates all identifiers + */ + public void update(Collection identifiers) + { + for(Identifier identifier : identifiers) + { + update(identifier); + } + } + + /** + * Removes/clears all identifiers from the collection + */ + public void clear() + { + Iterator it = mIdentifiers.iterator(); + + while(it.hasNext()) + { + notifyRemove(it.next()); + it.remove(); + } + } + + /** + * Removes all identifiers of the specified identifier class + */ + public void remove(IdentifierClass identifierClass) + { + Iterator it = mIdentifiers.iterator(); + + Identifier next = null; + + while(it.hasNext()) + { + next = it.next(); + + if(next.getIdentifierClass() == identifierClass) + { + it.remove(); + notifyRemove(next); + } + } + } + + /** + * Removes all identifiers of the specified form + */ + public void remove(Form form) + { + Iterator it = mIdentifiers.iterator(); + + Identifier next = null; + + while(it.hasNext()) + { + next = it.next(); + + if(next.getForm() == form) + { + it.remove(); + notifyRemove(next); + } + } + } + + /** + * Removes all identifiers of the specified role + */ + public void remove(Role role) + { + Iterator it = mIdentifiers.iterator(); + + Identifier next = null; + + while(it.hasNext()) + { + next = it.next(); + + if(next.getRole() == role) + { + it.remove(); + notifyRemove(next); + } + } + } + + /** + * Removes all identifiers of the specified identifier class, form and role + */ + public void remove(IdentifierClass identifierClass, Form form, Role role) + { + Iterator it = mIdentifiers.iterator(); + + Identifier next = null; + + while(it.hasNext()) + { + next = it.next(); + + if(next.getIdentifierClass() == identifierClass && next.getForm() == form && next.getRole() == role) + { + it.remove(); + notifyRemove(next); + } + } + } + + /** + * Removes all identifiers of the specified identifier class and role + */ + public void remove(IdentifierClass identifierClass, Role role) + { + Iterator it = mIdentifiers.iterator(); + + Identifier next = null; + + while(it.hasNext()) + { + next = it.next(); + + if(next.getIdentifierClass() == identifierClass && next.getRole() == role) + { + it.remove(); + notifyRemove(next); + } + } + } + + /** + * Implements the listener interface to receive notifications of identifier updates. This allows an + * identifier collection to maintain a synchronized state with a remote identifier collection. + * + * @param identifierUpdateNotification to add or remove an identifier + */ + @Override + public void receive(IdentifierUpdateNotification identifierUpdateNotification) + { + if(identifierUpdateNotification.isAdded()) + { + update(identifierUpdateNotification.getIdentifier()); + } + else if(identifierUpdateNotification.isRemoved()) + { + remove(identifierUpdateNotification.getIdentifier()); + } + else if(identifierUpdateNotification.isSilentAdd()) + { + silentUpdate(identifierUpdateNotification.getIdentifier()); + } + else if(identifierUpdateNotification.isSilentRemove()) + { + silentRemove(identifierUpdateNotification.getIdentifier()); + } + } + + /** + * Creates a copy of this collection and resets the updated flag so that future invocations of this + * method will return a copy with the updated flag set to false. + */ + public IdentifierCollection copyOf() + { + IdentifierCollection copy = new IdentifierCollection(getIdentifiers()); + copy.setUpdated(isUpdated()); + setUpdated(false); + return copy; + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/Role.java b/src/main/java/io/github/dsheirer/identifier/Role.java new file mode 100644 index 00000000..ce05669d --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/Role.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.identifier; + +/** + * Identifier role. Indicates the role of the entity in the communication. + */ +public enum Role +{ + BROADCAST, + FROM, + IDENTIFIER, + STATUS, + TO, + ANY; +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/AliasListConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/AliasListConfigurationIdentifier.java new file mode 100644 index 00000000..22e77878 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/AliasListConfigurationIdentifier.java @@ -0,0 +1,45 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; + +/** + * User specified alias list name for a decode configuration. + */ +public class AliasListConfigurationIdentifier extends ConfigurationStringIdentifier +{ + public AliasListConfigurationIdentifier(String value) + { + super(value, Form.ALIAS_LIST); + } + + @Override + public boolean isValid() + { + return getValue() != null && !getValue().isEmpty(); + } + + public static AliasListConfigurationIdentifier create(String value) + { + return new AliasListConfigurationIdentifier(value); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/ChannelDescriptorConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/ChannelDescriptorConfigurationIdentifier.java new file mode 100644 index 00000000..e9aae49c --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/ChannelDescriptorConfigurationIdentifier.java @@ -0,0 +1,57 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.protocol.Protocol; + +/** + * Configuration identifier that identifies a channel descriptor. This is primarily used to broadcast the channel + * descriptor for a traffic channel so that it is aware of what channel number and frequency it is decoding. + */ +public class ChannelDescriptorConfigurationIdentifier extends Identifier +{ + /** + * Constructs a channel descriptor configuration identifier + */ + public ChannelDescriptorConfigurationIdentifier(IChannelDescriptor value) + { + super(value, IdentifierClass.CONFIGURATION, Form.CHANNEL_DESCRIPTOR, Role.ANY); + } + + /** + * Protocol for the channel descriptor and the enclosing identifier instance. + */ + @Override + public Protocol getProtocol() + { + if(getValue() != null) + { + return getValue().getProtocol(); + } + + return Protocol.UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/ChannelNameConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/ChannelNameConfigurationIdentifier.java new file mode 100644 index 00000000..d23f5a96 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/ChannelNameConfigurationIdentifier.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; + +/** + * User specified channel name for a decode configuration. + */ +public class ChannelNameConfigurationIdentifier extends ConfigurationStringIdentifier +{ + public ChannelNameConfigurationIdentifier() + { + this(null); + } + + public ChannelNameConfigurationIdentifier(String value) + { + super(value, Form.CHANNEL); + } + + @Override + public boolean isValid() + { + return getValue() != null && !getValue().isEmpty(); + } + + public static ChannelNameConfigurationIdentifier create(String value) + { + return new ChannelNameConfigurationIdentifier(value); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationLongIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationLongIdentifier.java new file mode 100644 index 00000000..0434a827 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationLongIdentifier.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.longnumber.LongIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Decoder channel configuration identifier. + */ +public class ConfigurationLongIdentifier extends LongIdentifier +{ + public ConfigurationLongIdentifier(Long value, Form form) + { + super(value, IdentifierClass.CONFIGURATION, form, Role.ANY); + } + + @Override + public Protocol getProtocol() + { + return Protocol.UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationStringIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationStringIdentifier.java new file mode 100644 index 00000000..378c569c --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/ConfigurationStringIdentifier.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.string.StringIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Decoder channel configuration identifier. + */ +public class ConfigurationStringIdentifier extends StringIdentifier +{ + public ConfigurationStringIdentifier(String value, Form form) + { + super(value, IdentifierClass.CONFIGURATION, form, Role.ANY); + } + + @Override + public Protocol getProtocol() + { + return Protocol.UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/DecoderTypeConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/DecoderTypeConfigurationIdentifier.java new file mode 100644 index 00000000..ccba2a44 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/DecoderTypeConfigurationIdentifier.java @@ -0,0 +1,56 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.protocol.Protocol; + +public class DecoderTypeConfigurationIdentifier extends Identifier +{ + public DecoderTypeConfigurationIdentifier(DecoderType value) + { + super(value, IdentifierClass.CONFIGURATION, Form.DECODER_TYPE, Role.ANY); + } + + @Override + public Protocol getProtocol() + { + return Protocol.UNKNOWN; + } + + /** + * Creates a decoder state identifier from the decoder state value + */ + public static DecoderTypeConfigurationIdentifier create(DecoderType decoderType) + { + return new DecoderTypeConfigurationIdentifier(decoderType); + } + + @Override + public String toString() + { + return getValue().getShortDisplayString(); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/FrequencyConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/FrequencyConfigurationIdentifier.java new file mode 100644 index 00000000..0631e5df --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/FrequencyConfigurationIdentifier.java @@ -0,0 +1,39 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; + +/** + * Channel tuned frequency configuration identifier + */ +public class FrequencyConfigurationIdentifier extends ConfigurationLongIdentifier +{ + public FrequencyConfigurationIdentifier(Long value) + { + super(value, Form.CHANNEL_FREQUENCY); + } + + public static FrequencyConfigurationIdentifier create(Long value) + { + return new FrequencyConfigurationIdentifier(value); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/SiteConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/SiteConfigurationIdentifier.java new file mode 100644 index 00000000..6a0afbd1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/SiteConfigurationIdentifier.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; + +/** + * User specified site name for a decode configuration. + */ +public class SiteConfigurationIdentifier extends ConfigurationStringIdentifier +{ + public SiteConfigurationIdentifier() + { + this(null); + } + + public SiteConfigurationIdentifier(String value) + { + super(value, Form.SITE); + } + + @Override + public boolean isValid() + { + return getValue() != null && !getValue().isEmpty(); + } + + public static SiteConfigurationIdentifier create(String value) + { + return new SiteConfigurationIdentifier(value); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/configuration/SystemConfigurationIdentifier.java b/src/main/java/io/github/dsheirer/identifier/configuration/SystemConfigurationIdentifier.java new file mode 100644 index 00000000..e4dca465 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/configuration/SystemConfigurationIdentifier.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.configuration; + +import io.github.dsheirer.identifier.Form; + +/** + * User specified system name for a decode configuration. + */ +public class SystemConfigurationIdentifier extends ConfigurationStringIdentifier +{ + public SystemConfigurationIdentifier() + { + this(null); + } + + public SystemConfigurationIdentifier(String value) + { + super(value, Form.SYSTEM); + } + + @Override + public boolean isValid() + { + return getValue() != null && !getValue().isEmpty(); + } + + public static SystemConfigurationIdentifier create(String value) + { + return new SystemConfigurationIdentifier(value); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/decoder/DecoderLogicalChannelNameIdentifier.java b/src/main/java/io/github/dsheirer/identifier/decoder/DecoderLogicalChannelNameIdentifier.java new file mode 100644 index 00000000..8d2842f5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/decoder/DecoderLogicalChannelNameIdentifier.java @@ -0,0 +1,52 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.decoder; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.string.StringIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class DecoderLogicalChannelNameIdentifier extends StringIdentifier +{ + private Protocol mProtocol; + + public DecoderLogicalChannelNameIdentifier(String name, Protocol protocol) + { + super(name, IdentifierClass.DECODER, Form.CHANNEL_NAME, Role.BROADCAST); + mProtocol = protocol; + } + + @Override + public Protocol getProtocol() + { + return mProtocol; + } + + /** + * Creates a decoder state identifier from the decoder state value + */ + public static DecoderLogicalChannelNameIdentifier create(String name, Protocol protocol) + { + return new DecoderLogicalChannelNameIdentifier(name, protocol); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/decoder/DecoderStateIdentifier.java b/src/main/java/io/github/dsheirer/identifier/decoder/DecoderStateIdentifier.java new file mode 100644 index 00000000..41ccf413 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/decoder/DecoderStateIdentifier.java @@ -0,0 +1,66 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.decoder; + +import io.github.dsheirer.channel.state.State; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.protocol.Protocol; + +public class DecoderStateIdentifier extends Identifier +{ + public static final DecoderStateIdentifier ACTIVE = DecoderStateIdentifier.create(State.ACTIVE); + public static final DecoderStateIdentifier CALL = DecoderStateIdentifier.create(State.CALL); + public static final DecoderStateIdentifier CONTROL = DecoderStateIdentifier.create(State.CONTROL); + public static final DecoderStateIdentifier DATA = DecoderStateIdentifier.create(State.DATA); + public static final DecoderStateIdentifier ENCRYPTED = DecoderStateIdentifier.create(State.ENCRYPTED); + public static final DecoderStateIdentifier FADE = DecoderStateIdentifier.create(State.FADE); + public static final DecoderStateIdentifier IDLE = DecoderStateIdentifier.create(State.IDLE); + public static final DecoderStateIdentifier TEARDOWN = DecoderStateIdentifier.create(State.TEARDOWN); + public static final DecoderStateIdentifier RESET = DecoderStateIdentifier.create(State.RESET); + + public DecoderStateIdentifier(State value) + { + super(value, IdentifierClass.DECODER, Form.STATE, Role.ANY); + } + + @Override + public Protocol getProtocol() + { + return Protocol.UNKNOWN; + } + + /** + * Creates a decoder state identifier from the decoder state value + */ + public static DecoderStateIdentifier create(State state) + { + return new DecoderStateIdentifier(state); + } + + @Override + public String toString() + { + return getValue().getDisplayValue(); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKey.java b/src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKey.java new file mode 100644 index 00000000..67f8e152 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKey.java @@ -0,0 +1,93 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.encryption; + +import java.util.Objects; + +public class EncryptionKey implements Comparable +{ + private int mAlgorithm; + private int mKey; + + public EncryptionKey(int algorithm, int key) + { + mAlgorithm = algorithm; + mKey = key; + } + + public boolean isEncrypted() + { + return mAlgorithm != 0x80; + } + + public int getAlgorithm() + { + return mAlgorithm; + } + + public int getKey() + { + return mKey; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ALGORITHM:").append(getAlgorithm()); + sb.append(" KEY:").append(getKey()); + return sb.toString(); + } + + @Override + public int compareTo(EncryptionKey other) + { + if(getAlgorithm() == other.getKey()) + { + return Integer.compare(getKey(), other.getKey()); + } + else + { + return Integer.compare(getAlgorithm(), other.getAlgorithm()); + } + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + EncryptionKey that = (EncryptionKey)o; + return getAlgorithm() == that.getAlgorithm() && + getKey() == that.getKey(); + } + + @Override + public int hashCode() + { + return Objects.hash(getAlgorithm(), getKey()); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKeyIdentifier.java b/src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKeyIdentifier.java new file mode 100644 index 00000000..51176a2e --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/encryption/EncryptionKeyIdentifier.java @@ -0,0 +1,36 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.encryption; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; + +public abstract class EncryptionKeyIdentifier extends Identifier +{ + public EncryptionKeyIdentifier(EncryptionKey value, IdentifierClass identifierClass, Form form, Role role) + { + super(value, identifierClass, form, role); + } + + +} diff --git a/src/main/java/io/github/dsheirer/identifier/esn/ESNIdentifier.java b/src/main/java/io/github/dsheirer/identifier/esn/ESNIdentifier.java new file mode 100644 index 00000000..e83cda6d --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/esn/ESNIdentifier.java @@ -0,0 +1,66 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.esn; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.string.StringIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class ESNIdentifier extends StringIdentifier implements Comparable +{ + private Protocol mProtocol; + + public ESNIdentifier(String esn, Protocol protocol, Role role) + { + super(esn, IdentifierClass.USER, Form.ESN, role); + mProtocol = protocol; + } + + @Override + public Protocol getProtocol() + { + return mProtocol; + } + + public static ESNIdentifier create(String esn, Protocol protocol, Role role) + { + return new ESNIdentifier(esn, protocol, role); + } + + @Override + public int compareTo(ESNIdentifier o) + { + if(getValue() == null) + { + return 1; + } + else if(o.getValue() == null) + { + return -1; + } + else + { + return getValue().compareTo(o.getValue()); + } + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/integer/IntegerIdentifier.java b/src/main/java/io/github/dsheirer/identifier/integer/IntegerIdentifier.java new file mode 100644 index 00000000..8351de0e --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/integer/IntegerIdentifier.java @@ -0,0 +1,66 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.integer; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; + +import java.util.Objects; + +/** + * Integer identifier base class. + */ +public abstract class IntegerIdentifier extends Identifier +{ + public IntegerIdentifier(int value, IdentifierClass identifierClass, Form form, Role role) + { + super(value, identifierClass, form, role); + } + + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + IntegerIdentifier that = (IntegerIdentifier)o; + + return (getValue().intValue() == that.getValue().intValue()) && + getIdentifierClass() == that.getIdentifierClass() && + getForm() == that.getForm() && + getRole() == that.getRole(); + } + + @Override + public int hashCode() + { + return Objects.hash(getValue(), getIdentifierClass(), getForm(), getRole()); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Address.java b/src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Address.java new file mode 100644 index 00000000..f71474c3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Address.java @@ -0,0 +1,93 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.ipv4; + +import java.util.Objects; + +/** + * IPV4 Address + */ +public class IPV4Address +{ + private int mIPAddress; + + public IPV4Address(int ipAddress) + { + mIPAddress = ipAddress; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getOctet(0)); + sb.append("."); + sb.append(getOctet(1)); + sb.append("."); + sb.append(getOctet(2)); + sb.append("."); + sb.append(getOctet(3)); + return sb.toString(); + } + + /** + * Extracts the octet value from the IP address + * @param index of the octet where 0 is the MSB and 3 is the LSB + * @return octet value + */ + private int getOctet(int index) + { + switch(index) + { + case 0: + return ((mIPAddress & 0xFF000000) >> 24) & 0xFF; + case 1: + return ((mIPAddress & 0xFF0000) >> 16); + case 2: + return ((mIPAddress & 0xFF00) >> 8); + case 3: + return ((mIPAddress & 0xFF)); + default: + throw new IllegalArgumentException("Invalid Octet index: " + index); + } + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + IPV4Address that = (IPV4Address)o; + return mIPAddress == that.mIPAddress; + } + + @Override + public int hashCode() + { + return Objects.hash(mIPAddress); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Identifier.java b/src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Identifier.java new file mode 100644 index 00000000..94adcbd8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/ipv4/IPV4Identifier.java @@ -0,0 +1,34 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.ipv4; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; + +public abstract class IPV4Identifier extends Identifier +{ + public IPV4Identifier(IPV4Address value, Role role) + { + super(value, IdentifierClass.USER, Form.IPV4_ADDRESS, role); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/longnumber/LongIdentifier.java b/src/main/java/io/github/dsheirer/identifier/longnumber/LongIdentifier.java new file mode 100644 index 00000000..9b01ecf6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/longnumber/LongIdentifier.java @@ -0,0 +1,66 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.longnumber; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; + +import java.util.Objects; + +/** + * Long identifier base class. + */ +public abstract class LongIdentifier extends Identifier +{ + public LongIdentifier(Long value, IdentifierClass identifierClass, Form form, Role role) + { + super(value, identifierClass, form, role); + } + + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + LongIdentifier that = (LongIdentifier)o; + + return (getValue().longValue() == that.getValue().longValue()) && + getIdentifierClass() == that.getIdentifierClass() && + getForm() == that.getForm() && + getRole() == that.getRole(); + } + + @Override + public int hashCode() + { + return Objects.hash(getValue(), getIdentifierClass(), getForm(), getRole()); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/patch/PatchGroup.java b/src/main/java/io/github/dsheirer/identifier/patch/PatchGroup.java new file mode 100644 index 00000000..0ee60788 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/patch/PatchGroup.java @@ -0,0 +1,101 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.patch; + +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class PatchGroup +{ +// private final static Logger mLog = LoggerFactory.getLogger(PatchGroup.class); + + private TalkgroupIdentifier mPatchGroupIdentifier; + private List mPatchedGroupIdentifiers = new ArrayList<>(); + + public PatchGroup(TalkgroupIdentifier patchGroupIdentifier) + { + mPatchGroupIdentifier = patchGroupIdentifier; + } + + public TalkgroupIdentifier getPatchGroup() + { + return mPatchGroupIdentifier; + } + + public void addPatchedGroup(TalkgroupIdentifier patchedGroupIdentifier) + { + mPatchedGroupIdentifiers.add(patchedGroupIdentifier); + } + + public void addPatchedGroups(List patchedGroupIdentifiers) + { + for(TalkgroupIdentifier identifier : patchedGroupIdentifiers) + { + if(!mPatchedGroupIdentifiers.contains(identifier)) + { + mPatchedGroupIdentifiers.add(identifier); + } + } + } + + public List getPatchedGroupIdentifiers() + { + return mPatchedGroupIdentifiers; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("P:").append(getPatchGroup()); + + if(!getPatchedGroupIdentifiers().isEmpty()) + { + sb.append(" ").append(getPatchedGroupIdentifiers()); + } + + return sb.toString(); + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + PatchGroup that = (PatchGroup)o; + return Objects.equals(mPatchGroupIdentifier, that.mPatchGroupIdentifier); + } + + @Override + public int hashCode() + { + return Objects.hash(mPatchGroupIdentifier); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/patch/PatchGroupIdentifier.java b/src/main/java/io/github/dsheirer/identifier/patch/PatchGroupIdentifier.java new file mode 100644 index 00000000..299af56c --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/patch/PatchGroupIdentifier.java @@ -0,0 +1,34 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.patch; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; + +public abstract class PatchGroupIdentifier extends Identifier +{ + public PatchGroupIdentifier(PatchGroup value) + { + super(value, IdentifierClass.USER, Form.PATCH_GROUP, Role.TO); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/patch/PatchGroupManager.java b/src/main/java/io/github/dsheirer/identifier/patch/PatchGroupManager.java new file mode 100644 index 00000000..4c9ff34a --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/patch/PatchGroupManager.java @@ -0,0 +1,184 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.patch; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Manager for (temporary) patch groups and related identifiers. + */ +public class PatchGroupManager +{ + // private final static Logger mLog = LoggerFactory.getLogger(PatchGroupManager.class); + private Map mPatchGroupLastUpdatedMap = new HashMap<>(); + private Map mPatchGroupMap = new HashMap<>(); + + public PatchGroupManager() + { + } + + /** + * Adds the patch group identifier to the set of managed patch groups. If the patch group is already being + * managed, the new identifier is checked for additional patched talkgroups and they are added to the existing + * managed patch group. + * + * @param patchGroupIdentifier to add or update + */ + public synchronized void addPatchGroup(PatchGroupIdentifier patchGroupIdentifier) + { + PatchGroup update = patchGroupIdentifier.getValue(); + + if(update.getPatchGroup().getValue() > 0) + { + if(mPatchGroupMap.containsKey(update.getPatchGroup().getValue())) + { + PatchGroup current = mPatchGroupMap.get(update.getPatchGroup().getValue()).getValue(); + current.addPatchedGroups(update.getPatchedGroupIdentifiers()); + } + else + { + mPatchGroupMap.put(update.getPatchGroup().getValue(), patchGroupIdentifier); + } + } + } + + /** + * Adds any patch group identifiers contained in the list. + */ + public synchronized void addPatchGroups(List identifiers) + { + for(Identifier identifier : identifiers) + { + if(identifier instanceof PatchGroupIdentifier) + { + addPatchGroup((PatchGroupIdentifier)identifier); + } + } + } + + /** + * Removes the patch group from this manager if it is currently being managed. + * + * @param patchGroupIdentifier to remove + */ + public synchronized void removePatchGroup(PatchGroupIdentifier patchGroupIdentifier) + { + int id = patchGroupIdentifier.getValue().getPatchGroup().getValue(); + mPatchGroupMap.remove(id); + mPatchGroupLastUpdatedMap.remove(id); + } + + /** + * Removes any patch group identifiers contained in the list. + */ + public synchronized void removePatchGroups(List identifiers) + { + for(Identifier identifier : identifiers) + { + if(identifier instanceof PatchGroupIdentifier) + { + removePatchGroup((PatchGroupIdentifier)identifier); + } + } + } + + /** + * Updates the list of identifiers by replacing any talkgroups or patch groups with the current + * version of the patch group and a complete listing of the patched talkgroups. + * @param identifiers to update + * @return list of identifiers + */ + public List update(List identifiers) + { + List updated = new ArrayList<>(); + + for(Identifier identifier: identifiers) + { + updated.add(update(identifier)); + } + + return updated; + } + + /** + * Checks the TALKGROUP or PATCH GROUP identifier and replaces the identifier with the current patch group + * if the identifier matches a currently managed patch group. + * + * @param identifier for a talkgroup or a patch group. + * @return current patch group or the original identifier + */ + public Identifier update(Identifier identifier) + { + if(identifier == null) + { + return identifier; + } + + if(identifier.getIdentifierClass() == IdentifierClass.USER && identifier.getRole() == Role.TO) + { + switch(identifier.getForm()) + { + case TALKGROUP: + if(identifier instanceof TalkgroupIdentifier) + { + TalkgroupIdentifier tgi = (TalkgroupIdentifier)identifier; + + if(tgi.isGroup()) + { + int talkgroupId = ((TalkgroupIdentifier)identifier).getValue(); + + if(mPatchGroupMap.containsKey(talkgroupId)) + { + return mPatchGroupMap.get(talkgroupId); + } + } + } + break; + case PATCH_GROUP: + if(identifier instanceof PatchGroupIdentifier) + { + PatchGroupIdentifier patchGroupIdentifier = (PatchGroupIdentifier)identifier; + int patchGroupId = patchGroupIdentifier.getValue().getPatchGroup().getValue(); + + if(mPatchGroupMap.containsKey(patchGroupId)) + { + return mPatchGroupMap.get(patchGroupId); + } + else + { + addPatchGroup(patchGroupIdentifier); + } + } + break; + } + } + + return identifier; + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/string/StringIdentifier.java b/src/main/java/io/github/dsheirer/identifier/string/StringIdentifier.java new file mode 100644 index 00000000..b79b93ef --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/string/StringIdentifier.java @@ -0,0 +1,65 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.string; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; + +import java.util.Objects; + +/** + * String identifier base class + */ +public abstract class StringIdentifier extends Identifier +{ + public StringIdentifier(String value, IdentifierClass identifierClass, Form form, Role role) + { + super(value, identifierClass, form, role); + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + StringIdentifier that = (StringIdentifier)o; + + return (getValue().equals(that.getValue())) && + getIdentifierClass() == that.getIdentifierClass() && + getForm() == that.getForm() && + getRole() == that.getRole(); + } + + @Override + public int hashCode() + { + return Objects.hash(getValue(), getIdentifierClass(), getForm(), getRole()); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/talkgroup/LTRTalkgroup.java b/src/main/java/io/github/dsheirer/identifier/talkgroup/LTRTalkgroup.java new file mode 100644 index 00000000..7eee4f5a --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/talkgroup/LTRTalkgroup.java @@ -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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.talkgroup; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.protocol.Protocol; + +/** + * LTR talkgroup identifier + */ +public class LTRTalkgroup extends TalkgroupIdentifier implements Comparable +{ + public LTRTalkgroup(Integer talkgroup, Role role) + { + super(talkgroup, role, true); + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_NET; + } + + /** + * This identifier formatted as HH:GGG where H=home channel and G=group + */ + public String formatted() + { + return String.format("%02d-%03d", getHomeChannel(), getTalkgroup()); + } + + /** + * Home channel for the identifier + */ + public int getHomeChannel() + { + return (getValue() >> 8) & 0x1F; + } + + /** + * Talkgroup + */ + public int getTalkgroup() + { + return getValue() & 0xFF; + } + + /** + * Creates an LTR-Net identifier from the integer value that contains both the fleet and the ident with a TO role + */ + public static LTRTalkgroup create(int talkgroup) + { + return new LTRTalkgroup(talkgroup, Role.TO); + } + + @Override + public int compareTo(LTRTalkgroup o) + { + return getValue().compareTo(o.getValue()); + } +} diff --git a/src/main/java/io/github/dsheirer/identifier/talkgroup/TalkgroupIdentifier.java b/src/main/java/io/github/dsheirer/identifier/talkgroup/TalkgroupIdentifier.java new file mode 100644 index 00000000..a4102998 --- /dev/null +++ b/src/main/java/io/github/dsheirer/identifier/talkgroup/TalkgroupIdentifier.java @@ -0,0 +1,51 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.identifier.talkgroup; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; + +public abstract class TalkgroupIdentifier extends IntegerIdentifier +{ + private boolean mIsGroup; + + public TalkgroupIdentifier(Integer value, Role role, boolean isGroup) + { + super(value, IdentifierClass.USER, Form.TALKGROUP, role); + mIsGroup = isGroup; + } + + @Override + public boolean isValid() + { + return getValue() > 0; + } + + /** + * Indicates if this is a group or individual identifier + */ + public boolean isGroup() + { + return mIsGroup; + } +} diff --git a/src/main/java/io/github/dsheirer/map/IPlottableUpdateListener.java b/src/main/java/io/github/dsheirer/map/IPlottableUpdateListener.java new file mode 100644 index 00000000..02d81012 --- /dev/null +++ b/src/main/java/io/github/dsheirer/map/IPlottableUpdateListener.java @@ -0,0 +1,27 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.map; + +public interface IPlottableUpdateListener +{ + void entitiesUpdated(); + void addPlottableEntity(PlottableEntityHistory entity); + void removePlottableEntity(PlottableEntityHistory entity); +} diff --git a/src/main/java/io/github/dsheirer/map/MapPanel.java b/src/main/java/io/github/dsheirer/map/MapPanel.java index 17fcf983..a6c7abbf 100644 --- a/src/main/java/io/github/dsheirer/map/MapPanel.java +++ b/src/main/java/io/github/dsheirer/map/MapPanel.java @@ -18,6 +18,7 @@ ******************************************************************************/ package io.github.dsheirer.map; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.icon.IconManager; import io.github.dsheirer.settings.MapViewSetting; import io.github.dsheirer.settings.SettingsManager; @@ -30,10 +31,10 @@ import org.jdesktop.swingx.mapviewer.DefaultTileFactory; import org.jdesktop.swingx.mapviewer.GeoPosition; import org.jdesktop.swingx.mapviewer.TileFactoryInfo; -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; +import java.awt.EventQueue; -public class MapPanel extends JPanel implements PlottableUpdateListener +public class MapPanel extends JPanel implements IPlottableUpdateListener { private static final long serialVersionUID = 1L; @@ -42,11 +43,11 @@ public class MapPanel extends JPanel implements PlottableUpdateListener private JXMapViewer mMapViewer = new JXMapViewer(); private PlottableEntityPainter mMapPainter; - public MapPanel(MapService mapService, IconManager iconManager, SettingsManager settingsManager) + public MapPanel(MapService mapService, AliasModel aliasModel, IconManager iconManager, SettingsManager settingsManager) { mSettingsManager = settingsManager; mMapService = mapService; - mMapPainter = new PlottableEntityPainter(iconManager); + mMapPainter = new PlottableEntityPainter(aliasModel, iconManager); init(); } @@ -115,29 +116,20 @@ public class MapPanel extends JPanel implements PlottableUpdateListener @Override public void entitiesUpdated() { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mMapViewer.repaint(); - } - }); + EventQueue.invokeLater(() -> mMapViewer.repaint()); } @Override - public void addPlottableEntity(PlottableEntity entity) + public void addPlottableEntity(PlottableEntityHistory entity) { mMapPainter.addEntity(entity); - entitiesUpdated(); } @Override - public void removePlottableEntity(PlottableEntity entity) + public void removePlottableEntity(PlottableEntityHistory entity) { mMapPainter.removeEntity(entity); - entitiesUpdated(); } } diff --git a/src/main/java/io/github/dsheirer/map/MapService.java b/src/main/java/io/github/dsheirer/map/MapService.java index 80d53f77..0f695db8 100644 --- a/src/main/java/io/github/dsheirer/map/MapService.java +++ b/src/main/java/io/github/dsheirer/map/MapService.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,155 +15,78 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.map; -import io.github.dsheirer.alias.Alias; import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.awt.*; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.Map; -public class MapService implements Listener +public class MapService implements Listener { + private final static Logger mLog = LoggerFactory.getLogger(MapService.class); + private int mMaxHistory = 2; - private int mCullThresholdMinutes = 60; - - private static final Color sDEFAULT_COLOR = Color.BLACK; - - private List mListeners = new ArrayList(); - - private HashMap mEntities = - new HashMap(); - + private List mListeners = new ArrayList(); + private Map mEntityHistories = new HashMap<>(); private IconManager mIconManager; public MapService(IconManager resourceManager) { mIconManager = resourceManager; - -// scheduler.scheduleAtFixedRate( new CullThread(), 5, 5, TimeUnit.MINUTES ); - } - - /** - * Returns the max plottables (locations) history setting that is applied - * to all plottable entities - */ - public int getMaxHistory() - { - return mMaxHistory; - } - - /** - * Sets the max history trail for entities. Value is applied to all newly - * created PlottableEntity and all existing entities. - */ - public void setMaxHistory(int maxHistory) - { - mMaxHistory = maxHistory; - - /** - * Update any existing entities with the new max history value - */ - for(PlottableEntity entity : mEntities.values()) - { - entity.setMaxHistory(mMaxHistory); - } } @Override - public void receive(Message message) + public void receive(IDecodeEvent decodeEvent) { - Plottable plottable = message.getPlottable(); - - if(plottable != null) + if(decodeEvent instanceof PlottableDecodeEvent) { - PlottableEntity entity = mEntities.get(plottable.getID()); + PlottableDecodeEvent plottableDecodeEvent = (PlottableDecodeEvent)decodeEvent; + Identifier from = plottableDecodeEvent.getIdentifierCollection().getFromIdentifier(); - if(entity == null) + if(from != null) { - Alias alias = plottable.getAlias(); + PlottableEntityHistory entityHistory = mEntityHistories.get(from); - Color color = null; - - if(alias != null) + if(entityHistory == null) { - color = alias.getDisplayColor(); + entityHistory = new PlottableEntityHistory(from, plottableDecodeEvent); + mEntityHistories.put(from, entityHistory); + } + else + { + entityHistory.add(plottableDecodeEvent); } - if(color == null) + for(IPlottableUpdateListener listener : mListeners) { - color = sDEFAULT_COLOR; - } - - entity = new PlottableEntity(plottable, color); - - entity.setMaxHistory(mMaxHistory); - - mEntities.put(plottable.getID(), entity); - - for(PlottableUpdateListener listener : mListeners) - { - listener.addPlottableEntity(entity); + listener.addPlottableEntity(entityHistory); } } else { - entity.addPlottable(plottable); - - for(PlottableUpdateListener listener : mListeners) - { - listener.entitiesUpdated(); - ; - } + mLog.warn("Received plottable decode event that does not contain a FROM identifier - cannot plot"); } } } - public void addListener(PlottableUpdateListener listener) + public void addListener(IPlottableUpdateListener listener) { mListeners.add(listener); } - public void removeListener(PlottableUpdateListener listener) + public void removeListener(IPlottableUpdateListener listener) { mListeners.remove(listener); } - - public class CullThread implements Runnable - { - @Override - public void run() - { - long cutoffTime = System.currentTimeMillis() - - (mCullThresholdMinutes * 1000); - - Set ids = Collections.unmodifiableSet(mEntities.keySet()); - - for(String id : ids) - { - PlottableEntity entity = mEntities.get(id); - - if(entity.getCurrentPlottable().getTimestamp() < cutoffTime) - { - mEntities.remove(id); - - /** - * Let all listeners know we've removed (culled) an entity - */ - for(PlottableUpdateListener listener : mListeners) - { - listener.removePlottableEntity(entity); - } - } - } - } - } } diff --git a/src/main/java/io/github/dsheirer/map/Plottable.java b/src/main/java/io/github/dsheirer/map/Plottable.java deleted file mode 100644 index 3c8a0113..00000000 --- a/src/main/java/io/github/dsheirer/map/Plottable.java +++ /dev/null @@ -1,138 +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 - ******************************************************************************/ -package io.github.dsheirer.map; - -import io.github.dsheirer.alias.Alias; -import org.jdesktop.swingx.mapviewer.GeoPosition; - -import java.util.HashMap; - -public class Plottable implements Comparable -{ - private long mTimestamp; - private GeoPosition mGeoPosition; - private String mID; - private Alias mAlias; - - HashMap mAttributes = new HashMap(); - - public Plottable() - { - } - - public Plottable( long timestamp, - GeoPosition position, - String id, - Alias alias, - HashMap attributes ) - { - this( timestamp, position, id, alias ); - mAttributes = attributes; - } - - public Plottable( long timestamp, - GeoPosition position, - String id, - Alias alias ) - { - mTimestamp = timestamp; - mGeoPosition = position; - mID = id; - mAlias = alias; - } - - public long getTimestamp() - { - return mTimestamp; - } - - public void setTimestamp( long timestamp ) - { - mTimestamp = timestamp; - } - - public GeoPosition getGeoPosition() - { - return mGeoPosition; - } - - public void setGeoPosition( GeoPosition position ) - { - mGeoPosition = position; - } - - public String getID() - { - return mID; - } - - public void setID( String id ) - { - mID = id; - } - - public Alias getAlias() - { - return mAlias; - } - - public void setAlias( Alias alias ) - { - mAlias = alias; - } - - public HashMap getAttributes() - { - return mAttributes; - } - - public void addAttribute( String key, String value ) - { - mAttributes.put( key, value ); - } - - public void removeAttribute( String key ) - { - mAttributes.remove( key ); - } - - public void clearAttributes() - { - mAttributes.clear(); - } - - /** - * Comparisons for sorting of plottables is based on timestamps - */ - @Override - public int compareTo( Plottable o ) - { - if( mTimestamp == o.mTimestamp ) - { - return 0; - } - else if( mTimestamp < o.mTimestamp ) - { - return -1; - } - else - { - return 1; - } - } -} diff --git a/src/main/java/io/github/dsheirer/map/PlottableEntity.java b/src/main/java/io/github/dsheirer/map/PlottableEntity.java deleted file mode 100644 index 62bfb474..00000000 --- a/src/main/java/io/github/dsheirer/map/PlottableEntity.java +++ /dev/null @@ -1,170 +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 - ******************************************************************************/ -package io.github.dsheirer.map; - -import io.github.dsheirer.alias.Alias; -import org.jdesktop.swingx.mapviewer.GeoPosition; - -import java.awt.*; -import java.util.Collections; -import java.util.Set; -import java.util.TreeSet; - -public class PlottableEntity implements Comparable -{ - private TreeSet mPlottables = new TreeSet(); - - private Alias mAlias; - private Color mColor; - private String mID; - - private int mMaxHistory = 2; - - /** - * Constructs the entity using the group and alias from the plottable and - * adds the plottable as the first plottable entry in the history - */ - public PlottableEntity( Plottable plottable, Color color ) - { - addPlottable( plottable ); - mColor = color; - } - - public int getMaxHistory() - { - return mMaxHistory; - } - - public void setMaxHistory( int maxHistory ) - { - if( maxHistory >= 1 ) - { - mMaxHistory = maxHistory; - } - } - - public Plottable getCurrentPlottable() - { - return mPlottables.last(); - } - - public GeoPosition getCurrentGeoPosition() - { - return getCurrentPlottable().getGeoPosition(); - } - - /** - * Adds the plottable to this entity - */ - public void addPlottable( Plottable plottable ) - { - /** - * Grab the entity settings from the latest plot - */ - mAlias = plottable.getAlias(); - mID = plottable.getID(); - - /** - * Add the plottable to the list - */ - mPlottables.add( plottable ); - - while( mPlottables.size() > mMaxHistory ) - { - mPlottables.remove( mPlottables.first() ); - } - } - - public String getLabel() - { - if( mAlias == null ) - { - return mID; - } - else - { - return mAlias.getName(); - } - } - - public String getMapIconName() - { - if( mAlias != null ) - { - return mAlias.getIconName(); - } - else - { - return null; - } - } - - /** - * Color for rendering this entity (label, route, etc.) - */ - public Color getColor() - { - return mColor; - } - - public void setColor( Color color ) - { - mColor = color; - } - - /** - * Default id to use in the label - */ - public String getID() - { - return mID; - } - - /** - * Gets the alias for this entity - */ - public Alias getAlias() - { - return mAlias; - } - - /** - * Returns an unmodifiable sorted (time order) list of plottables - */ - public Set getPlottables() - { - return Collections.unmodifiableSet( mPlottables ); - } - - /** - * Clears all plottables from this entity - */ - public void clear() - { - mPlottables.clear(); - } - - /** - * Compares by alias name - */ - @Override - public int compareTo( PlottableEntity o ) - { - return this.getAlias().getName().compareTo( o.getAlias().getName() ); - } -} diff --git a/src/main/java/io/github/dsheirer/map/PlottableEntityHistory.java b/src/main/java/io/github/dsheirer/map/PlottableEntityHistory.java new file mode 100644 index 00000000..f99c9a9f --- /dev/null +++ b/src/main/java/io/github/dsheirer/map/PlottableEntityHistory.java @@ -0,0 +1,82 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.map; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; +import org.jdesktop.swingx.mapviewer.GeoPosition; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Plottable entity history with location history. + */ +public class PlottableEntityHistory +{ + private List mLocationHistory = new ArrayList<>(); + private PlottableDecodeEvent mCurrentEvent; + private Identifier mIdentifier; + + /** + * Constructs a plottable entity history + */ + public PlottableEntityHistory(Identifier identifier, PlottableDecodeEvent event) + { + mIdentifier = identifier; + add(event); + } + + /** + * Location history for this entity + */ + public List getLocationHistory() + { + return Collections.unmodifiableList(mLocationHistory); + } + + /** + * Identifier for this plottable + */ + public Identifier getIdentifier() + { + return mIdentifier; + } + + /** + * Identifier collection from the latest event for this plottable + */ + public IdentifierCollection getIdentifierCollection() + { + return mCurrentEvent.getIdentifierCollection(); + } + + /** + * Updates the entity history with a location from the latest decode event + */ + public void add(PlottableDecodeEvent event) + { + mCurrentEvent = event; + mLocationHistory.add(event.getLocation()); + } +} diff --git a/src/main/java/io/github/dsheirer/map/PlottableEntityPainter.java b/src/main/java/io/github/dsheirer/map/PlottableEntityPainter.java index bc7960aa..8a3042a4 100644 --- a/src/main/java/io/github/dsheirer/map/PlottableEntityPainter.java +++ b/src/main/java/io/github/dsheirer/map/PlottableEntityPainter.java @@ -1,7 +1,8 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -15,15 +16,17 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.map; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.icon.IconManager; import org.jdesktop.swingx.JXMapViewer; import org.jdesktop.swingx.painter.AbstractPainter; -import java.awt.*; +import java.awt.Graphics2D; +import java.awt.Rectangle; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -31,21 +34,21 @@ import java.util.Set; public class PlottableEntityPainter extends AbstractPainter { private PlottableEntityRenderer mRenderer; - private Set mEntities = new HashSet(); + private Set mEntities = new HashSet<>(); - public PlottableEntityPainter(IconManager iconManager) + public PlottableEntityPainter(AliasModel aliasModel, IconManager iconManager) { - mRenderer = new PlottableEntityRenderer(iconManager); + mRenderer = new PlottableEntityRenderer(aliasModel, iconManager); setAntialiasing(true); setCacheable(false); } - public void addEntity(PlottableEntity entity) + public void addEntity(PlottableEntityHistory entity) { mEntities.add(entity); } - public void removeEntity(PlottableEntity entity) + public void removeEntity(PlottableEntityHistory entity) { mEntities.remove(entity); } @@ -55,7 +58,7 @@ public class PlottableEntityPainter extends AbstractPainter mEntities.clear(); } - private Set getEntities() + private Set getEntities() { return Collections.unmodifiableSet(mEntities); } @@ -67,9 +70,9 @@ public class PlottableEntityPainter extends AbstractPainter g.translate(-viewportBounds.getX(), -viewportBounds.getY()); - Set entities = getEntities(); + Set entities = getEntities(); - for(PlottableEntity entity : entities) + for(PlottableEntityHistory entity : entities) { mRenderer.paintPlottableEntity(g, map, entity, true); } diff --git a/src/main/java/io/github/dsheirer/map/PlottableEntityRenderer.java b/src/main/java/io/github/dsheirer/map/PlottableEntityRenderer.java index d7136de9..a5309aa5 100644 --- a/src/main/java/io/github/dsheirer/map/PlottableEntityRenderer.java +++ b/src/main/java/io/github/dsheirer/map/PlottableEntityRenderer.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,96 +15,117 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.map; +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.icon.IconManager; import org.jdesktop.swingx.JXMapViewer; +import org.jdesktop.swingx.mapviewer.GeoPosition; -import javax.swing.*; -import java.awt.*; +import javax.swing.ImageIcon; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; import java.awt.geom.Point2D; -import java.util.Set; +import java.util.List; public class PlottableEntityRenderer { + private AliasModel mAliasModel; private IconManager mIconManager; - public PlottableEntityRenderer(IconManager iconManager) + public PlottableEntityRenderer(AliasModel aliasModel, IconManager iconManager) { + mAliasModel = aliasModel; mIconManager = iconManager; } - public void paintPlottableEntity(Graphics2D g, JXMapViewer viewer, PlottableEntity entity, boolean antiAliasing) + public void paintPlottableEntity(Graphics2D g, JXMapViewer viewer, PlottableEntityHistory entity, boolean antiAliasing) { - Graphics2D graphics = (Graphics2D) g.create(); + List locationHistory = entity.getLocationHistory(); - if(antiAliasing) + if(!locationHistory.isEmpty() && locationHistory.get(locationHistory.size() - 1).isValid()) { - graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); + Graphics2D graphics = (Graphics2D)g.create(); + + if(antiAliasing) + { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + + Alias alias = getAlias(entity); + /** + * Use the entity's preferred color for lines and labels + */ + Color color = (alias != null ? alias.getDisplayColor() : Color.YELLOW); + graphics.setColor(color); + + /** + * Paint the route first, so the icon and label overlay it + */ + paintRoute(graphics, viewer, entity, color); + + + /** + * Convert the lat/long geoposition to an x/y point on the viewer + */ + + Point2D point = viewer.getTileFactory().geoToPixel(locationHistory.get(locationHistory.size() - 1), viewer.getZoom()); + + /** + * Paint the icon at the current location + */ + ImageIcon icon = getIcon(alias); + paintIcon(graphics, point, icon); + + /** + * Paint the label offset to the right of the icon + */ + String label = (alias != null ? alias.getName() : entity.getIdentifier().toString()); + paintLabel(graphics, point, label, (int)(icon.getIconWidth() / 2), 0); + + /** + * Cleanup + */ + graphics.dispose(); + } + } + + /** + * Optional alias that matches the identifier for the entity history + */ + private Alias getAlias(PlottableEntityHistory entityHistory) + { + AliasList aliasList = mAliasModel.getAliasList(entityHistory.getIdentifierCollection()); + + if(aliasList != null) + { + return aliasList.getAlias(entityHistory.getIdentifier()); } - /** - * Use the entity's preferred color for lines and labels - */ - graphics.setColor(entity.getColor()); - - /** - * Convert the lat/long geoposition to an x/y point on the viewer - */ - Point2D point = viewer.getTileFactory().geoToPixel( - entity.getCurrentGeoPosition(), viewer.getZoom()); - - /** - * Paint the route first, so the icon and label overlay it - */ - paintRoute(graphics, viewer, entity); - - /** - * Paint the icon at the current location - */ - ImageIcon icon = getIcon(entity); - - paintIcon(graphics, point, icon); - - /** - * Paint the label offset to the right of the icon - */ - paintLabel(graphics, point, entity, (int) (icon.getIconWidth() / 2), 0); - - /** - * Cleanup - */ - graphics.dispose(); + return null; } - private ImageIcon getIcon(PlottableEntity entity) + private ImageIcon getIcon(Alias alias) { - return mIconManager.getIcon(entity.getMapIconName(), IconManager.DEFAULT_ICON_SIZE); + String iconName = (alias != null ? alias.getIconName() : null); + return mIconManager.getIcon(iconName, IconManager.DEFAULT_ICON_SIZE); } - private void paintIcon(Graphics2D graphics, - Point2D point, - ImageIcon icon) + private void paintIcon(Graphics2D graphics, Point2D point, ImageIcon icon) { - graphics.drawImage(icon.getImage(), - (int) point.getX() - (icon.getIconWidth() / 2), - (int) point.getY() - (icon.getIconHeight() / 2), - null); - + graphics.drawImage(icon.getImage(), (int)point.getX() - (icon.getIconWidth() / 2), + (int)point.getY() - (icon.getIconHeight() / 2), null); } - private void paintLabel(Graphics2D graphics, - Point2D point, - PlottableEntity entity, - int xOffset, - int yOffset) + private void paintLabel(Graphics2D graphics, Point2D point, String label, int xOffset, int yOffset) { - graphics.drawString(entity.getLabel(), - (int) point.getX() + xOffset, - (int) point.getY() + yOffset); + graphics.drawString(label, (int)point.getX() + xOffset, (int)point.getY() + yOffset); } /** @@ -111,47 +133,41 @@ public class PlottableEntityRenderer * using black as a wider background route, and the entity's preferred color * as a narrower foreground route. */ - private void paintRoute(Graphics2D graphics, - JXMapViewer viewer, - PlottableEntity entity) + private void paintRoute(Graphics2D graphics, JXMapViewer viewer, PlottableEntityHistory entity, Color color) { - Set plottables = entity.getPlottables(); + List locations = entity.getLocationHistory(); - if(plottables.size() > 1) + if(!locations.isEmpty()) { // Draw the route with a black background line graphics.setColor(Color.BLACK); graphics.setStroke(new BasicStroke(3)); - drawRoute(plottables, graphics, viewer); + drawRoute(locations, graphics, viewer); // Draw the route again, in the entity's preferred color - graphics.setColor(entity.getColor()); + graphics.setColor(color); graphics.setStroke(new BasicStroke(1)); - drawRoute(plottables, graphics, viewer); + drawRoute(locations, graphics, viewer); } } /** * Draws a route from a list of plottables */ - private void drawRoute(Set plottables, - Graphics2D g, - JXMapViewer viewer) + private void drawRoute(List locations, Graphics2D g, JXMapViewer viewer) { Point2D lastPoint = null; - for(Plottable plottable : plottables) + for(GeoPosition location : locations) { // convert geo-coordinate to world bitmap pixel - Point2D currentPoint = viewer.getTileFactory() - .geoToPixel(plottable.getGeoPosition(), viewer.getZoom()); + Point2D currentPoint = viewer.getTileFactory().geoToPixel(location, viewer.getZoom()); if(lastPoint != null) { - g.drawLine((int) lastPoint.getX(), (int) lastPoint.getY(), - (int) currentPoint.getX(), (int) currentPoint.getY()); + g.drawLine((int)lastPoint.getX(), (int)lastPoint.getY(), (int)currentPoint.getX(), (int)currentPoint.getY()); } lastPoint = currentPoint; diff --git a/src/main/java/io/github/dsheirer/map/PlottableUpdateListener.java b/src/main/java/io/github/dsheirer/map/PlottableUpdateListener.java deleted file mode 100644 index cd24aca0..00000000 --- a/src/main/java/io/github/dsheirer/map/PlottableUpdateListener.java +++ /dev/null @@ -1,27 +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 - ******************************************************************************/ -package io.github.dsheirer.map; - -public interface PlottableUpdateListener -{ - public void entitiesUpdated(); - - public void addPlottableEntity( PlottableEntity entity ); - - public void removePlottableEntity( PlottableEntity entity ); -} diff --git a/src/main/java/io/github/dsheirer/message/IBitErrorProvider.java b/src/main/java/io/github/dsheirer/message/IBitErrorProvider.java new file mode 100644 index 00000000..89ee0d15 --- /dev/null +++ b/src/main/java/io/github/dsheirer/message/IBitErrorProvider.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.message; + +/** + * Interface for elements to provide a number of bits processed and the number of errors that were + * corrected. + */ +public interface IBitErrorProvider +{ + /** + * Indicates the number of bits that have been processed. + */ + int getBitsProcessedCount(); + + /** + * Indicates the number of errors contained in the bits that have been processed. + */ + int getBitErrorsCount(); +} diff --git a/src/main/java/io/github/dsheirer/message/IMessage.java b/src/main/java/io/github/dsheirer/message/IMessage.java new file mode 100644 index 00000000..6af72d71 --- /dev/null +++ b/src/main/java/io/github/dsheirer/message/IMessage.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.message; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.protocol.Protocol; + +import java.util.List; + +public interface IMessage +{ + /** + * Timestamp for when the message was transmitted or processed + * @return milliseconds since epoch timestamp + */ + long getTimestamp(); + + /** + * Indicates if the message is valid and has passed all error detection and correction. + */ + boolean isValid(); + + /** + * Protocol name associated with the message + */ + Protocol getProtocol(); + + /** + * List of identifiers present in the message + * @return list of identifiers or an empty list + */ + List getIdentifiers(); +} diff --git a/src/main/java/io/github/dsheirer/message/IMessageListener.java b/src/main/java/io/github/dsheirer/message/IMessageListener.java index b8d26c9b..42164a2c 100644 --- a/src/main/java/io/github/dsheirer/message/IMessageListener.java +++ b/src/main/java/io/github/dsheirer/message/IMessageListener.java @@ -4,5 +4,5 @@ import io.github.dsheirer.sample.Listener; public interface IMessageListener { - public Listener getMessageListener(); + Listener getMessageListener(); } diff --git a/src/main/java/io/github/dsheirer/message/IMessageProvider.java b/src/main/java/io/github/dsheirer/message/IMessageProvider.java index 7f3fbfd4..1f800d3b 100644 --- a/src/main/java/io/github/dsheirer/message/IMessageProvider.java +++ b/src/main/java/io/github/dsheirer/message/IMessageProvider.java @@ -4,6 +4,6 @@ import io.github.dsheirer.sample.Listener; public interface IMessageProvider { - public void setMessageListener( Listener listener ); - public void removeMessageListener(); + void setMessageListener(Listener listener); + void removeMessageListener(); } diff --git a/src/main/java/io/github/dsheirer/message/Message.java b/src/main/java/io/github/dsheirer/message/Message.java index 1ff480e2..57ead0df 100644 --- a/src/main/java/io/github/dsheirer/message/Message.java +++ b/src/main/java/io/github/dsheirer/message/Message.java @@ -1,115 +1,68 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 + * sdr-trunk + * 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 + * ******************************************************************************/ package io.github.dsheirer.message; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.protocol.Protocol; -import java.util.Collections; -import java.util.List; - -public abstract class Message +/** + * IMessage implementation. + */ +public abstract class Message implements IMessage { - protected long mTimeReceived; - - public Message() - { - mTimeReceived = System.currentTimeMillis(); - } + private long mTimestamp; - public long getTimeReceived() - { - return mTimeReceived; - } + /** + * Constructs a new message using current system time as the timestamp. + */ + public Message() + { + mTimestamp = System.currentTimeMillis(); + } - /** - * Map plottable - */ - public Plottable getPlottable() - { -//TODO: move this to an IPlottable interface that only gets implemented as needed - return null; - } + /** + * Constructs a new message using the argument timestamp. + * + * @param timestamp for the message + */ + public Message(long timestamp) + { + mTimestamp = timestamp; + } - /** - * Decoded textual representation of the message - */ - public abstract String toString(); - - /** - * Indicates if the message is valid and has passed crc/integrity checks - */ - public abstract boolean isValid(); + /** + * Timestamp when the message was received or processed. This timestamp should be as close to + * accurate as possible. + */ + public long getTimestamp() + { + return mTimestamp; + } - /** - * Status of the CRC check of the message - */ - public abstract String getErrorStatus(); + /** + * Decoded textual representation of the message + */ + public abstract String toString(); - /** - * Parsed Message - * @return - */ - public abstract String getMessage(); + /** + * Indicates if the message is valid and has passed crc/integrity checks + */ + public abstract boolean isValid(); - /** - * Raw ( 0 & 1 ) message bits - */ - public abstract String getBinaryMessage(); - - - /** - * Decoded protocol - */ - public abstract String getProtocol(); - - /** - * Event - call, data, idle, etc. - */ - public abstract String getEventType(); - - /** - * Formatted from identifier - */ - public abstract String getFromID(); - - /** - * From identifier alias (from AliasManager) - */ - public abstract Alias getFromIDAlias(); - - /** - * Formatted to identifier - */ - public abstract String getToID(); - - /** - * To identifier alias (from AliasManager) - * @return - */ - public abstract Alias getToIDAlias(); - - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - return Collections.EMPTY_LIST; - } + /** + * Decoded protocol + */ + public abstract Protocol getProtocol(); } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/message/SyncLossMessage.java b/src/main/java/io/github/dsheirer/message/SyncLossMessage.java new file mode 100644 index 00000000..9740b980 --- /dev/null +++ b/src/main/java/io/github/dsheirer/message/SyncLossMessage.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.message; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.protocol.Protocol; + +import java.util.Collections; +import java.util.List; + +/** + * Empty message to convey that a number of bits were processed with no sync or valid message detected. + * + * This message supports periodic updates to external processor(s) that may be tracking bit error rates. + */ +public class SyncLossMessage extends Message +{ + private int mBitsProcessed; + private Protocol mProtocol; + + /** + * Constructs a sync loss message. + * + * @param timestamp of the message + * @param bitsProcessed without a sync pattern detection of message decode + */ + public SyncLossMessage(long timestamp, int bitsProcessed, Protocol protocol) + { + super(timestamp); + mBitsProcessed = bitsProcessed; + mProtocol = protocol; + } + + /** + * Indicates the number of bits that were processed without a sync dectection or message decode + */ + public int getBitsProcessed() + { + return mBitsProcessed; + } + + @Override + public String toString() + { + return "<-> SYNC LOSS - BITS PROCESSED [" + getBitsProcessed() + "]"; + } + + @Override + public boolean isValid() + { + return true; + } + + @Override + public Protocol getProtocol() + { + return mProtocol; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java index 11255615..661cbea7 100644 --- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java +++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,34 +15,35 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.module; +import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.audio.IAudioPacketListener; import io.github.dsheirer.audio.IAudioPacketProvider; import io.github.dsheirer.audio.squelch.ISquelchStateListener; import io.github.dsheirer.audio.squelch.ISquelchStateProvider; import io.github.dsheirer.audio.squelch.SquelchState; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; -import io.github.dsheirer.channel.metadata.IAttributeChangeRequestListener; -import io.github.dsheirer.channel.metadata.IAttributeChangeRequestProvider; import io.github.dsheirer.channel.state.ChannelState; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.IDecoderStateEventListener; import io.github.dsheirer.channel.state.IDecoderStateEventProvider; -import io.github.dsheirer.controller.channel.Channel.ChannelType; +import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.ChannelEvent; import io.github.dsheirer.controller.channel.IChannelEventListener; import io.github.dsheirer.controller.channel.IChannelEventProvider; +import io.github.dsheirer.identifier.IdentifierUpdateListener; +import io.github.dsheirer.identifier.IdentifierUpdateNotification; +import io.github.dsheirer.identifier.IdentifierUpdateProvider; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessageListener; import io.github.dsheirer.message.IMessageProvider; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.event.CallEventModel; -import io.github.dsheirer.module.decode.event.ICallEventListener; -import io.github.dsheirer.module.decode.event.ICallEventProvider; +import io.github.dsheirer.module.decode.event.DecodeEventModel; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEventListener; +import io.github.dsheirer.module.decode.event.IDecodeEventProvider; import io.github.dsheirer.module.decode.event.MessageActivityModel; import io.github.dsheirer.module.log.EventLogger; import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder; @@ -91,7 +93,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * Optional: if you want to reuse the processing chain with a new sample source, * invoke the following method sequence: stop(), setSource(), start() */ -public class ProcessingChain implements IChannelEventListener +public class ProcessingChain implements Listener { private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class); @@ -99,40 +101,40 @@ public class ProcessingChain implements IChannelEventListener private ReusableBufferBroadcaster mDemodulatedAudioBufferBroadcaster = new ReusableBufferBroadcaster(); private ReusableBufferBroadcaster mBasebandComplexBufferBroadcaster = new ReusableBufferBroadcaster(); private ReusableBufferBroadcaster mDemodulatedBitstreamBufferBroadcaster = new ReusableBufferBroadcaster(); - private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); - private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); + private Broadcaster mDecodeEventBroadcaster = new Broadcaster<>(); private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); private Broadcaster mHeartbeatBroadcaster = new Broadcaster<>(); + private Broadcaster mIdentifierUpdateNotificationBroadcaster = new Broadcaster<>(); private Broadcaster mSourceEventBroadcaster = new Broadcaster<>(); - private Broadcaster mMessageBroadcaster = new Broadcaster<>(); + private Broadcaster mMessageBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); private AtomicBoolean mRunning = new AtomicBoolean(); protected Source mSource; private List mModules = new ArrayList<>(); - private CallEventModel mCallEventModel; + private DecodeEventModel mDecodeEventModel; private ChannelState mChannelState; private MessageActivityModel mMessageActivityModel; /** * Creates a processing chain for managing a set of modules * - * @param channelType + * @param channel */ - public ProcessingChain(ChannelType channelType) + public ProcessingChain(Channel channel, AliasModel aliasModel) { - mChannelState = new ChannelState(channelType); + mChannelState = new ChannelState(channel, aliasModel); addModule(mChannelState); - mCallEventModel = new CallEventModel(); - addCallEventListener(mCallEventModel); + mDecodeEventModel = new DecodeEventModel(); + addDecodeEventListener(mDecodeEventModel); } - public CallEventModel getCallEventModel() + public DecodeEventModel getDecodeEventModel() { - return mCallEventModel; + return mDecodeEventModel; } public ChannelState getChannelState() @@ -164,7 +166,7 @@ public class ProcessingChain implements IChannelEventListener mModules.clear(); mAudioPacketBroadcaster.dispose(); - mCallEventBroadcaster.dispose(); + mDecodeEventBroadcaster.dispose(); mChannelEventBroadcaster.dispose(); mBasebandComplexBufferBroadcaster.dispose(); mDemodulatedBitstreamBufferBroadcaster.dispose(); @@ -230,7 +232,7 @@ public class ProcessingChain implements IChannelEventListener { if(module instanceof DecoderState) { - decoderStates.add((DecoderState) module); + decoderStates.add((DecoderState)module); } } @@ -288,29 +290,29 @@ public class ProcessingChain implements IChannelEventListener */ private void registerListeners(Module module) { - if(module instanceof IAttributeChangeRequestListener) + if(module instanceof IdentifierUpdateListener) { - mAttributeChangeRequestBroadcaster.addListener(((IAttributeChangeRequestListener)module).getAttributeChangeRequestListener()); + mIdentifierUpdateNotificationBroadcaster.addListener(((IdentifierUpdateListener)module).getIdentifierUpdateListener()); } if(module instanceof IAudioPacketListener) { - mAudioPacketBroadcaster.addListener(((IAudioPacketListener) module).getAudioPacketListener()); + mAudioPacketBroadcaster.addListener(((IAudioPacketListener)module).getAudioPacketListener()); } - if(module instanceof ICallEventListener) + if(module instanceof IDecodeEventListener) { - mCallEventBroadcaster.addListener(((ICallEventListener) module).getCallEventListener()); + mDecodeEventBroadcaster.addListener(((IDecodeEventListener)module).getDecodeEventListener()); } if(module instanceof IChannelEventListener) { - mChannelEventBroadcaster.addListener(((IChannelEventListener) module).getChannelEventListener()); + mChannelEventBroadcaster.addListener(((IChannelEventListener)module).getChannelEventListener()); } if(module instanceof IDecoderStateEventListener) { - mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener()); + mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener)module).getDecoderStateListener()); } if(module instanceof IHeartbeatListener) @@ -320,7 +322,7 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof IMessageListener) { - mMessageBroadcaster.addListener(((IMessageListener) module).getMessageListener()); + mMessageBroadcaster.addListener(((IMessageListener)module).getMessageListener()); } if(module instanceof IReusableBufferListener) @@ -350,7 +352,7 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof ISquelchStateListener) { - mSquelchStateBroadcaster.addListener(((ISquelchStateListener) module).getSquelchStateListener()); + mSquelchStateBroadcaster.addListener(((ISquelchStateListener)module).getSquelchStateListener()); } } @@ -360,29 +362,29 @@ public class ProcessingChain implements IChannelEventListener */ private void unregisterListeners(Module module) { - if(module instanceof IAttributeChangeRequestListener) + if(module instanceof IdentifierUpdateListener) { - mAttributeChangeRequestBroadcaster.removeListener(((IAttributeChangeRequestListener) module).getAttributeChangeRequestListener()); + mIdentifierUpdateNotificationBroadcaster.removeListener(((IdentifierUpdateListener)module).getIdentifierUpdateListener()); } if(module instanceof IAudioPacketListener) { - mAudioPacketBroadcaster.removeListener(((IAudioPacketListener) module).getAudioPacketListener()); + mAudioPacketBroadcaster.removeListener(((IAudioPacketListener)module).getAudioPacketListener()); } - if(module instanceof ICallEventListener) + if(module instanceof IDecodeEventListener) { - mCallEventBroadcaster.removeListener(((ICallEventListener) module).getCallEventListener()); + mDecodeEventBroadcaster.removeListener(((IDecodeEventListener)module).getDecodeEventListener()); } if(module instanceof IChannelEventListener) { - mChannelEventBroadcaster.removeListener(((IChannelEventListener) module).getChannelEventListener()); + mChannelEventBroadcaster.removeListener(((IChannelEventListener)module).getChannelEventListener()); } if(module instanceof IDecoderStateEventListener) { - mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener()); + mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener)module).getDecoderStateListener()); } if(module instanceof IHeartbeatListener) @@ -397,27 +399,27 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof IReusableByteBufferListener) { - mDemodulatedBitstreamBufferBroadcaster.removeListener(((IReusableByteBufferListener) module).getReusableByteBufferListener()); + mDemodulatedBitstreamBufferBroadcaster.removeListener(((IReusableByteBufferListener)module).getReusableByteBufferListener()); } if(module instanceof IReusableComplexBufferListener) { - mBasebandComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener()); + mBasebandComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener)module).getReusableComplexBufferListener()); } if(module instanceof ISourceEventListener) { - mSourceEventBroadcaster.removeListener(((ISourceEventListener) module).getSourceEventListener()); + mSourceEventBroadcaster.removeListener(((ISourceEventListener)module).getSourceEventListener()); } if(module instanceof IMessageListener) { - mMessageBroadcaster.removeListener(((IMessageListener) module).getMessageListener()); + mMessageBroadcaster.removeListener(((IMessageListener)module).getMessageListener()); } if(module instanceof ISquelchStateListener) { - mSquelchStateBroadcaster.removeListener(((ISquelchStateListener) module).getSquelchStateListener()); + mSquelchStateBroadcaster.removeListener(((ISquelchStateListener)module).getSquelchStateListener()); } } @@ -427,29 +429,29 @@ public class ProcessingChain implements IChannelEventListener */ private void registerProviders(Module module) { - if(module instanceof IAttributeChangeRequestProvider) + if(module instanceof IdentifierUpdateProvider) { - ((IAttributeChangeRequestProvider)module).setAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster); + ((IdentifierUpdateProvider)module).setIdentifierUpdateListener(mIdentifierUpdateNotificationBroadcaster); } if(module instanceof IAudioPacketProvider) { - ((IAudioPacketProvider) module).setAudioPacketListener(mAudioPacketBroadcaster); + ((IAudioPacketProvider)module).setAudioPacketListener(mAudioPacketBroadcaster); } - if(module instanceof ICallEventProvider) + if(module instanceof IDecodeEventProvider) { - ((ICallEventProvider) module).addCallEventListener(mCallEventBroadcaster); + ((IDecodeEventProvider)module).addDecodeEventListener(mDecodeEventBroadcaster); } if(module instanceof IChannelEventProvider) { - ((IChannelEventProvider) module).setChannelEventListener(mChannelEventBroadcaster); + ((IChannelEventProvider)module).setChannelEventListener(mChannelEventBroadcaster); } if(module instanceof IDecoderStateEventProvider) { - ((IDecoderStateEventProvider) module).setDecoderStateListener(mDecoderStateEventBroadcaster); + ((IDecoderStateEventProvider)module).setDecoderStateListener(mDecoderStateEventBroadcaster); } if(module instanceof IHeartbeatProvider) @@ -459,12 +461,12 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof IMessageProvider) { - ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); + ((IMessageProvider)module).setMessageListener(mMessageBroadcaster); } if(module instanceof IMessageProvider) { - ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); + ((IMessageProvider)module).setMessageListener(mMessageBroadcaster); } if(module instanceof IReusableByteBufferProvider) @@ -479,12 +481,12 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof ISourceEventProvider) { - ((ISourceEventProvider) module).setSourceEventListener(mSourceEventBroadcaster); + ((ISourceEventProvider)module).setSourceEventListener(mSourceEventBroadcaster); } if(module instanceof ISquelchStateProvider) { - ((ISquelchStateProvider) module).setSquelchStateListener(mSquelchStateBroadcaster); + ((ISquelchStateProvider)module).setSquelchStateListener(mSquelchStateBroadcaster); } } @@ -494,14 +496,14 @@ public class ProcessingChain implements IChannelEventListener */ private void unregisterProviders(Module module) { - if(module instanceof IAttributeChangeRequestProvider) + if(module instanceof IdentifierUpdateProvider) { - ((IAttributeChangeRequestProvider)module).removeAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster); + ((IdentifierUpdateProvider)module).removeIdentifierUpdateListener(); } if(module instanceof IAudioPacketProvider) { - ((IAudioPacketProvider) module).setAudioPacketListener(null); + ((IAudioPacketProvider)module).setAudioPacketListener(null); } if(module instanceof IReusableByteBufferProvider) @@ -509,19 +511,19 @@ public class ProcessingChain implements IChannelEventListener ((IReusableByteBufferProvider)module).removeBufferListener(mDemodulatedBitstreamBufferBroadcaster); } - if(module instanceof ICallEventProvider) + if(module instanceof IDecodeEventProvider) { - ((ICallEventProvider) module).removeCallEventListener(mCallEventBroadcaster); + ((IDecodeEventProvider)module).removeDecodeEventListener(mDecodeEventBroadcaster); } if(module instanceof IChannelEventProvider) { - ((IChannelEventProvider) module).setChannelEventListener(null); + ((IChannelEventProvider)module).removeChannelEventListener(); } if(module instanceof IDecoderStateEventProvider) { - ((IDecoderStateEventProvider) module).setDecoderStateListener(null); + ((IDecoderStateEventProvider)module).setDecoderStateListener(null); } if(module instanceof IHeartbeatProvider) @@ -531,12 +533,12 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof IMessageProvider) { - ((IMessageProvider) module).setMessageListener(null); + ((IMessageProvider)module).setMessageListener(null); } if(module instanceof IMessageProvider) { - ((IMessageProvider) module).setMessageListener(null); + ((IMessageProvider)module).setMessageListener(null); } if(module instanceof IReusableBufferProvider) @@ -546,12 +548,12 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof ISourceEventProvider) { - ((ISourceEventProvider) module).setSourceEventListener(null); + ((ISourceEventProvider)module).setSourceEventListener(null); } if(module instanceof ISquelchStateProvider) { - ((ISquelchStateProvider) module).setSquelchStateListener(null); + ((ISquelchStateProvider)module).setSquelchStateListener(null); } } @@ -574,23 +576,23 @@ public class ProcessingChain implements IChannelEventListener //Setup the channel state to monitor source overflow conditions mSource.setOverflowListener(mChannelState); - /* Register with the source to receive sample data. Setup a - * timer task to process the buffer queues 50 times a second - * (every 20 ms) */ + /* Register with the source to receive sample data. Setup a + * timer task to process the buffer queues 50 times a second + * (every 20 ms) */ switch(mSource.getSampleType()) { case COMPLEX: - ((ComplexSource) mSource).setListener(mBasebandComplexBufferBroadcaster); + ((ComplexSource)mSource).setListener(mBasebandComplexBufferBroadcaster); break; case REAL: - ((RealSource) mSource).setListener(mDemodulatedAudioBufferBroadcaster); + ((RealSource)mSource).setListener(mDemodulatedAudioBufferBroadcaster); break; default: throw new IllegalArgumentException("Unrecognized source " + "sample type - cannot start processing chain"); } - - /* Start each of the modules */ + + /* Start each of the modules */ for(Module module : mModules) { try @@ -628,10 +630,10 @@ public class ProcessingChain implements IChannelEventListener switch(mSource.getSampleType()) { case COMPLEX: - ((ComplexSource) mSource).removeListener(mBasebandComplexBufferBroadcaster); + ((ComplexSource)mSource).removeListener(mBasebandComplexBufferBroadcaster); break; case REAL: - ((RealSource) mSource).removeListener(mDemodulatedAudioBufferBroadcaster); + ((RealSource)mSource).removeListener(mDemodulatedAudioBufferBroadcaster); break; default: throw new IllegalArgumentException("Unrecognized source sample type - cannot start processing " + @@ -641,7 +643,7 @@ public class ProcessingChain implements IChannelEventListener mSource = null; } - /* Stop each of the remaining modules */ + /* Stop each of the remaining modules */ for(Module module : mModules) { module.stop(); @@ -711,16 +713,16 @@ public class ProcessingChain implements IChannelEventListener } /** - * Adds the listener to receive call events from all modules. + * Adds the listener to receive decode events from all modules. */ - public void addCallEventListener(Listener listener) + public void addDecodeEventListener(Listener listener) { - mCallEventBroadcaster.addListener(listener); + mDecodeEventBroadcaster.addListener(listener); } - public void removeCallEventListener(Listener listener) + public void removeDecodeEventListener(Listener listener) { - mCallEventBroadcaster.removeListener(listener); + mDecodeEventBroadcaster.removeListener(listener); } /** @@ -757,7 +759,7 @@ public class ProcessingChain implements IChannelEventListener /** * Adds the listener to receive decoded messages from all decoders. */ - public void addMessageListener(Listener listener) + public void addMessageListener(Listener listener) { mMessageBroadcaster.addListener(listener); } @@ -765,9 +767,9 @@ public class ProcessingChain implements IChannelEventListener /** * Adds the list of listeners to receive decoded messages from all decoders. */ - public void addMessageListeners(List> listeners) + public void addMessageListeners(List> listeners) { - for(Listener listener : listeners) + for(Listener listener : listeners) { mMessageBroadcaster.addListener(listener); } @@ -776,13 +778,14 @@ public class ProcessingChain implements IChannelEventListener /** * Removes the listener from receiving decoded messages from all decoders. */ - public void removeMessageListener(Listener listener) + public void removeMessageListener(Listener listener) { mMessageBroadcaster.removeListener(listener); } /** * Adds the listener to receive frequency change events from the processing chain + * * @param listener to receive events */ public void addFrequencyChangeListener(Listener listener) @@ -792,6 +795,7 @@ public class ProcessingChain implements IChannelEventListener /** * Removes the listener from receiving frequency change events + * * @param listener to remove */ public void removeFrequencyChangeListener(Listener listener) @@ -828,9 +832,22 @@ public class ProcessingChain implements IChannelEventListener mDemodulatedAudioBufferBroadcaster.removeListener(listener); } + /** + * Primary method for the channel processing manager to broadcast channel events, namely traffic channel processing + * stop events so that traffic channel manager(s) can maintain a correct state of traffic channels. + */ @Override - public Listener getChannelEventListener() + public void receive(ChannelEvent channelEvent) { - return mChannelEventBroadcaster; + mChannelEventBroadcaster.broadcast(channelEvent); + } + + /** + * Broadcasts an identifier update notification to all modules + * @param updateNotification to broadcast + */ + public void receive(IdentifierUpdateNotification updateNotification) + { + mIdentifierUpdateNotificationBroadcaster.broadcast(updateNotification); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/Decoder.java b/src/main/java/io/github/dsheirer/module/decode/Decoder.java index 00859c78..9cedf003 100644 --- a/src/main/java/io/github/dsheirer/module/decode/Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/Decoder.java @@ -17,6 +17,7 @@ ******************************************************************************/ package io.github.dsheirer.module.decode; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessageProvider; import io.github.dsheirer.message.Message; import io.github.dsheirer.module.Module; @@ -25,8 +26,8 @@ import io.github.dsheirer.sample.Listener; public abstract class Decoder extends Module implements IMessageProvider { /* This has to be a broadcaster in order for references to persist */ - private Listener mMessageDistributor = new MessageDistributor(); - protected Listener mMessageListener; + private Listener mMessageDistributor = new MessageDistributor(); + protected Listener mMessageListener; /** * Decoder - parent class for all decoders, demodulators and components. @@ -66,7 +67,7 @@ public abstract class Decoder extends Module implements IMessageProvider * Adds a listener for receiving decoded messages from this decoder */ @Override - public void setMessageListener(Listener listener) + public void setMessageListener(Listener listener) { mMessageListener = listener; } @@ -84,7 +85,7 @@ public abstract class Decoder extends Module implements IMessageProvider /** * Message listener for receiving the output from this decoder */ - protected Listener getMessageListener() + protected Listener getMessageListener() { return mMessageDistributor; } @@ -92,10 +93,10 @@ public abstract class Decoder extends Module implements IMessageProvider /** * Distributes/forwards messages from sub-class decoder implementations to the registered message listener. */ - public class MessageDistributor implements Listener + public class MessageDistributor implements Listener { @Override - public void receive(Message message) + public void receive(IMessage message) { if(mMessageListener != null) { diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java index 9e06f168..a5694007 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,20 +15,17 @@ * * 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; import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.alias.action.AliasActionManager; import io.github.dsheirer.audio.AudioModule; -import io.github.dsheirer.channel.metadata.Metadata; import io.github.dsheirer.channel.state.AlwaysUnsquelchedDecoderState; -import io.github.dsheirer.channel.traffic.TrafficChannelManager; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.Channel.ChannelType; -import io.github.dsheirer.controller.channel.ChannelModel; -import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.controller.channel.map.ChannelMap; import io.github.dsheirer.controller.channel.map.ChannelMapModel; import io.github.dsheirer.filter.AllPassFilter; @@ -35,7 +33,7 @@ import io.github.dsheirer.filter.FilterSet; import io.github.dsheirer.filter.IFilter; import io.github.dsheirer.gui.editor.EmptyValidatingEditor; import io.github.dsheirer.gui.editor.ValidatingEditor; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.MessageDirection; import io.github.dsheirer.module.Module; import io.github.dsheirer.module.decode.am.AMDecoder; @@ -67,16 +65,17 @@ import io.github.dsheirer.module.decode.mpt1327.MPT1327Decoder; import io.github.dsheirer.module.decode.mpt1327.MPT1327DecoderEditor; import io.github.dsheirer.module.decode.mpt1327.MPT1327DecoderState; import io.github.dsheirer.module.decode.mpt1327.MPT1327MessageFilter; +import io.github.dsheirer.module.decode.mpt1327.MPT1327TrafficChannelManager; import io.github.dsheirer.module.decode.mpt1327.Sync; import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM; import io.github.dsheirer.module.decode.nbfm.NBFMDecoder; import io.github.dsheirer.module.decode.nbfm.NBFMDecoderEditor; import io.github.dsheirer.module.decode.p25.DecodeConfigP25Phase1; -import io.github.dsheirer.module.decode.p25.P25Decoder.Modulation; import io.github.dsheirer.module.decode.p25.P25DecoderC4FM; import io.github.dsheirer.module.decode.p25.P25DecoderEditor; import io.github.dsheirer.module.decode.p25.P25DecoderLSM; import io.github.dsheirer.module.decode.p25.P25DecoderState; +import io.github.dsheirer.module.decode.p25.P25TrafficChannelManager; import io.github.dsheirer.module.decode.p25.audio.P25AudioModule; import io.github.dsheirer.module.decode.p25.message.filter.P25MessageFilterSet; import io.github.dsheirer.module.decode.passport.DecodeConfigPassport; @@ -110,37 +109,31 @@ public class DecoderFactory * * @return list of configured decoders */ - public static List getModules(ChannelModel channelModel, - ChannelMapModel channelMapModel, - ChannelProcessingManager channelProcessingManager, - AliasModel aliasModel, - Channel channel, - Metadata metadata) + public static List getModules(ChannelMapModel channelMapModel, Channel channel, AliasModel aliasModel) { - - /* Get the optional alias list for the decode modules to use */ - AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName()); - - List modules = getPrimaryModules(channelModel, channelMapModel, channelProcessingManager, aliasList, - channel, metadata); - - modules.addAll(getAuxiliaryDecoders(channel.getAuxDecodeConfiguration(), aliasList)); - + List modules = getPrimaryModules(channelMapModel, channel, aliasModel); + modules.addAll(getAuxiliaryDecoders(channel.getAuxDecodeConfiguration())); return modules; } /** * Constructs a primary decoder as specified in the decode configuration */ - public static List getPrimaryModules(ChannelModel channelModel, - ChannelMapModel channelMapModel, - ChannelProcessingManager channelProcessingManager, - AliasList aliasList, - Channel channel, - Metadata metadata) + public static List getPrimaryModules(ChannelMapModel channelMapModel, Channel channel, AliasModel aliasModel) { List modules = new ArrayList(); + //Adds an alias action manager when there are alias actions specified + if(channel.getAliasListName() != null && !channel.getAliasListName().isEmpty()) + { + AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName()); + + if(aliasList != null && aliasList.hasAliasActions()) + { + modules.add(new AliasActionManager(aliasList)); + } + } + ChannelType channelType = channel.getChannelType(); /* Baseband low-pass filter pass and stop frequencies */ @@ -151,7 +144,7 @@ public class DecoderFactory case AM: modules.add(new AMDecoder(decodeConfig)); modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.AM, channel.getName())); - modules.add(new AudioModule(metadata)); + modules.add(new AudioModule()); if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) { modules.add(new AMDemodulatorModule(AM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); @@ -160,87 +153,92 @@ public class DecoderFactory case NBFM: modules.add(new NBFMDecoder(decodeConfig)); modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.NBFM, channel.getName())); - modules.add(new AudioModule(metadata)); + modules.add(new AudioModule()); if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) { modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); } break; case LTR_STANDARD: - MessageDirection direction = ((DecodeConfigLTRStandard) decodeConfig).getMessageDirection(); - modules.add(new LTRStandardDecoder(aliasList, direction)); - modules.add(new LTRStandardDecoderState(aliasList)); - modules.add(new AudioModule(metadata)); + MessageDirection direction = ((DecodeConfigLTRStandard)decodeConfig).getMessageDirection(); + modules.add(new LTRStandardDecoder(null, direction)); + modules.add(new LTRStandardDecoderState()); + modules.add(new AudioModule()); if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) { modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); } break; case LTR_NET: - modules.add(new LTRNetDecoder((DecodeConfigLTRNet) decodeConfig, aliasList)); - modules.add(new LTRNetDecoderState(aliasList)); - modules.add(new AudioModule(metadata)); + modules.add(new LTRNetDecoder((DecodeConfigLTRNet)decodeConfig)); + modules.add(new LTRNetDecoderState()); + modules.add(new AudioModule()); if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) { modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); } break; case MPT1327: - DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327) decodeConfig; + DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)decodeConfig; ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName()); Sync sync = mptConfig.getSync(); - modules.add(new MPT1327Decoder(aliasList, sync)); - modules.add(new MPT1327DecoderState(aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000)); - modules.add(new AudioModule(metadata)); + modules.add(new MPT1327Decoder(sync)); + modules.add(new AudioModule()); if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) { modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); } + long timeout = mptConfig.getCallTimeout() * 1000; + if(channelType == ChannelType.STANDARD) { - modules.add(new TrafficChannelManager(channelModel, decodeConfig, - channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), - (aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize())); + MPT1327TrafficChannelManager trafficChannelManager = new MPT1327TrafficChannelManager(channel, channelMap); + modules.add(trafficChannelManager); + modules.add(new MPT1327DecoderState(trafficChannelManager, channelType, timeout)); + } + else + { + modules.add(new MPT1327DecoderState(channelType, timeout)); } break; case PASSPORT: - modules.add(new PassportDecoder(decodeConfig, aliasList)); - modules.add(new PassportDecoderState(aliasList)); - modules.add(new AudioModule(metadata)); + modules.add(new PassportDecoder(decodeConfig)); + modules.add(new PassportDecoderState()); + modules.add(new AudioModule()); if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER) { modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); } break; case P25_PHASE1: - DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1) decodeConfig; + DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)decodeConfig; - Modulation modulation = p25Config.getModulation(); - - switch(modulation) + switch(p25Config.getModulation()) { case C4FM: - modules.add(new P25DecoderC4FM(aliasList)); - modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM, - p25Config.getIgnoreDataCalls())); + modules.add(new P25DecoderC4FM()); break; case CQPSK: - modules.add(new P25DecoderLSM(aliasList)); - modules.add(new P25DecoderState(aliasList, channelType, Modulation.CQPSK, p25Config.getIgnoreDataCalls())); + modules.add(new P25DecoderLSM()); break; default: - throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + modulation + "]"); + throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + + p25Config.getModulation() + "]"); } if(channelType == ChannelType.STANDARD) { - modules.add(new TrafficChannelManager(channelModel, decodeConfig, - channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), - (aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize())); + P25TrafficChannelManager trafficChannelManager = new P25TrafficChannelManager(channel); + modules.add(trafficChannelManager); + modules.add(new P25DecoderState(channel, trafficChannelManager)); + } + else + { + modules.add(new P25DecoderState(channel)); } - modules.add(new P25AudioModule(metadata)); + modules.add(new P25AudioModule()); break; default: throw new IllegalArgumentException("Unknown decoder type [" + decodeConfig.getDecoderType().toString() + "]"); @@ -253,11 +251,9 @@ public class DecoderFactory * Constructs a list of auxiliary decoders, as specified in the configuration * * @param config - auxiliary configuration - * @param aliasList - optional alias list * @return - list of auxiliary decoders */ - public static List getAuxiliaryDecoders(AuxDecodeConfiguration config, - AliasList aliasList) + public static List getAuxiliaryDecoders(AuxDecodeConfiguration config) { List modules = new ArrayList<>(); @@ -268,24 +264,23 @@ public class DecoderFactory switch(auxDecoder) { case FLEETSYNC2: - modules.add(new Fleetsync2Decoder(aliasList)); - modules.add(new Fleetsync2DecoderState(aliasList)); + modules.add(new Fleetsync2Decoder()); + modules.add(new Fleetsync2DecoderState()); break; case MDC1200: - modules.add(new MDCDecoder(aliasList)); - modules.add(new MDCDecoderState(aliasList)); + modules.add(new MDCDecoder()); + modules.add(new MDCDecoderState()); break; case LJ_1200: - modules.add(new LJ1200Decoder(aliasList)); - modules.add(new LJ1200DecoderState(aliasList)); + modules.add(new LJ1200Decoder()); + modules.add(new LJ1200DecoderState()); break; case TAIT_1200: - modules.add(new Tait1200Decoder(aliasList)); - modules.add(new Tait1200DecoderState(aliasList)); + modules.add(new Tait1200Decoder()); + modules.add(new Tait1200DecoderState()); break; default: - throw new IllegalArgumentException("Unrecognized auxiliary " - + "decoder type [" + auxDecoder + "]"); + throw new IllegalArgumentException("Unrecognized auxiliary decoder type [" + auxDecoder + "]"); } } } @@ -297,9 +292,9 @@ public class DecoderFactory * Assembles a filter set containing filters for the primary channel * decoder and each of the auxiliary decoders */ - public static FilterSet getMessageFilters(List modules) + public static FilterSet getMessageFilters(List modules) { - FilterSet filterSet = new FilterSet<>(); + FilterSet filterSet = new FilterSet<>(); for(Module module : modules) { @@ -312,7 +307,7 @@ public class DecoderFactory /* If we don't have any filters, add an ALL-PASS filter */ if(filterSet.getFilters().isEmpty()) { - filterSet.addFilter(new AllPassFilter()); + filterSet.addFilter(new AllPassFilter<>()); } return filterSet; @@ -322,9 +317,9 @@ public class DecoderFactory * Returns a set of IMessageFilter objects (FilterSets or Filters) that * can process all of the messages produced by the specified decoder type. */ - public static List> getMessageFilter(DecoderType decoder) + public static List> getMessageFilter(DecoderType decoder) { - ArrayList> filters = new ArrayList>(); + ArrayList> filters = new ArrayList<>(); switch(decoder) { @@ -424,21 +419,19 @@ public class DecoderFactory switch(config.getDecoderType()) { case AM: - DecodeConfigAM originalAM = (DecodeConfigAM) config; - DecodeConfigAM copyAM = new DecodeConfigAM(); - return copyAM; + return new DecodeConfigAM(); case LTR_NET: - DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet) config; + DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet)config; DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet(); copyLTRNet.setMessageDirection(originalLTRNet.getMessageDirection()); return copyLTRNet; case LTR_STANDARD: - DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard) config; + DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard)config; DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard(); copyLTRStandard.setMessageDirection(originalLTRStandard.getMessageDirection()); return copyLTRStandard; case MPT1327: - DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327) config; + DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327)config; DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327(); copyMPT.setCallTimeout(originalMPT.getCallTimeout()); copyMPT.setChannelMapName(originalMPT.getChannelMapName()); @@ -446,20 +439,16 @@ public class DecoderFactory copyMPT.setTrafficChannelPoolSize(originalMPT.getTrafficChannelPoolSize()); return copyMPT; case NBFM: - DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM) config; - DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM(); - return copyNBFM; + return new DecodeConfigNBFM(); case P25_PHASE1: - DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1) config; + DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1)config; DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1(); copyP25.setIgnoreDataCalls(originalP25.getIgnoreDataCalls()); copyP25.setModulation(originalP25.getModulation()); copyP25.setTrafficChannelPoolSize(originalP25.getTrafficChannelPoolSize()); return copyP25; case PASSPORT: - DecodeConfigPassport originalPass = (DecodeConfigPassport) config; - DecodeConfigPassport copyPass = new DecodeConfigPassport(); - return copyPass; + return new DecodeConfigPassport(); default: throw new IllegalArgumentException("Unrecognized decoder configuration type:" + config.getDecoderType()); } @@ -469,12 +458,12 @@ public class DecoderFactory } /** - * Decoder(s) that support bitstreams. + * Decoder(s) that support bitstreams indicating that they produce ReusableByteBuffers of decoded bits. * * @return set of decoders that support bitstreams. */ public static EnumSet getBitstreamDecoders() { - return EnumSet.of(DecoderType.P25_PHASE1); + return EnumSet.of(DecoderType.P25_PHASE1, DecoderType.MPT1327); } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderType.java b/src/main/java/io/github/dsheirer/module/decode/DecoderType.java index 261428f2..1c7733d1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderType.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderType.java @@ -1,20 +1,26 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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; +import io.github.dsheirer.protocol.Protocol; + import java.util.ArrayList; import java.util.EnumSet; @@ -24,27 +30,29 @@ import java.util.EnumSet; public enum DecoderType { //Primary Decoders - AM("AM", "AM"), - LTR_STANDARD("LTR-Standard", "LTR"), - LTR_NET("LTR-Net", "LTR-Net"), - MPT1327("MPT1327", "MPT1327"), - NBFM("NBFM", "NBFM"), - PASSPORT("Passport", "Passport"), - P25_PHASE1("P25 Phase I", "P25-1"), + AM("AM", "AM", Protocol.UNKNOWN), + LTR_STANDARD("LTR-Standard", "LTR", Protocol.LTR_STANDARD), + LTR_NET("LTR-Net", "LTR-Net", Protocol.LTR_NET), + MPT1327("MPT1327", "MPT1327", Protocol.MPT1327), + NBFM("NBFM", "NBFM", Protocol.UNKNOWN), + PASSPORT("Passport", "Passport", Protocol.PASSPORT), + P25_PHASE1("P25 Phase I", "P25-1", Protocol.APCO25), //Auxiliary Decoders - FLEETSYNC2("Fleetsync II", "Fleetsync2"), - LJ_1200("LJ1200 173.075", "LJ1200"), - MDC1200("MDC1200", "MDC1200"), - TAIT_1200("Tait 1200", "Tait 1200"); + FLEETSYNC2("Fleetsync II", "Fleetsync2", Protocol.FLEETSYNC), + LJ_1200("LJ1200 173.075", "LJ1200", Protocol.LOJACK), + MDC1200("MDC1200", "MDC1200", Protocol.MDC1200), + TAIT_1200("Tait 1200", "Tait 1200", Protocol.TAIT1200); private String mDisplayString; private String mShortDisplayString; + private Protocol mProtocol; - DecoderType(String displayString, String shortDisplayString) + DecoderType(String displayString, String shortDisplayString, Protocol protocol) { mDisplayString = displayString; mShortDisplayString = shortDisplayString; + mProtocol = protocol; } /** @@ -76,6 +84,11 @@ public enum DecoderType return decoders; } + public Protocol getProtocol() + { + return mProtocol; + } + public String getDisplayString() { return mDisplayString; diff --git a/src/main/java/io/github/dsheirer/module/decode/event/ActivitySummaryProvider.java b/src/main/java/io/github/dsheirer/module/decode/event/ActivitySummaryProvider.java index a94e7ea9..c3a97b43 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/ActivitySummaryProvider.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/ActivitySummaryProvider.java @@ -17,10 +17,11 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.event; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.Message; import io.github.dsheirer.sample.Listener; -public interface ActivitySummaryProvider extends Listener +public interface ActivitySummaryProvider extends Listener { public String getActivitySummary(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/event/CallEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/CallEvent.java index a0126273..73ee4a5a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/CallEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/CallEvent.java @@ -20,9 +20,12 @@ package io.github.dsheirer.module.decode.event; import io.github.dsheirer.alias.Alias; import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.DecoderType; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; public abstract class CallEvent implements Comparable @@ -40,8 +43,31 @@ public abstract class CallEvent implements Comparable protected String mToID; protected String mDetails; + //New call event format member variables + protected IChannelDescriptor mChannelDescriptor; + protected Collection mIdentifiers; + private boolean mValid = true; + /** + * Constructs a call event + * @param decoder type + * @param callEventType description + * @param channelDescriptor that identifies the channel and frequency value(s) + * @param identifiers for to, from, comments, etc. + */ + public CallEvent(DecoderType decoder, CallEventType callEventType, IChannelDescriptor channelDescriptor, Collection identifiers) + { + mDecoderType = decoder; + mCallEventType = callEventType; + mChannelDescriptor = channelDescriptor; + mIdentifiers = identifiers; + } + + /** + * Legacy call event constructor + */ + @Deprecated //Remove once all decoder states have been updated to new constructor public CallEvent(DecoderType decoder, CallEventType callEventType, AliasList aliasList, String fromID, String toID, String details) { @@ -53,6 +79,14 @@ public abstract class CallEvent implements Comparable mDetails = details; } + /** + * Identifier collection for this call event + */ + public Collection getIdentifiers() + { + return mIdentifiers; + } + /** * Indicates if this event is valid. Use this method to mark a previously * dispatched event as invalid and resend it so that any downstream @@ -168,7 +202,7 @@ public abstract class CallEvent implements Comparable public static String getCSVHeader() { - return "START_DATE,START_TIME,END_DATE,END_TIME,DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,CHANNEL," + + return "START_DATE,START_TIME,END_DATE,END_TIME,DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,CHANNEL_NUMBER," + "FREQUENCY,DETAILS"; } @@ -252,6 +286,7 @@ public abstract class CallEvent implements Comparable public enum CallEventType { ANNOUNCEMENT("Announcement"), + AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"), CALL("Call"), CALL_ALERT("Call Alert"), CALL_DETECT("Call Detect"), @@ -262,6 +297,7 @@ public abstract class CallEvent implements Comparable CALL_TIMEOUT("Call Timeout"), COMMAND("Command"), DATA_CALL("Data Call"), + DATA_PACKET("Data Packet"), DEREGISTER("Deregister"), EMERGENCY("EMERGENCY"), ENCRYPTED_CALL("Encrypted Call"), @@ -270,6 +306,7 @@ public abstract class CallEvent implements Comparable GROUP_CALL("Group Call"), ID_ANI("ANI"), ID_UNIQUE("Unique ID"), + IP_PACKET("IP Packet"), NOTIFICATION("Notification"), PAGE("Page"), PATCH_GROUP_ADD("Patch Group Add"), @@ -279,10 +316,14 @@ public abstract class CallEvent implements Comparable REGISTER("Register"), REGISTER_ESN("ESN"), RESPONSE("Response"), + RESPONSE_PACKET("Response Packet"), SDM("Short Data Message"), + SNDCP_PACKET("SNDCP Packet"), STATION_ID("Station ID"), STATUS("Status"), + TCP_PACKET("TCP/IP Packet"), TELEPHONE_INTERCONNECT("Telephone Interconnect"), + UDP_PACKET("UDP/IP Packet"), UNIT_TO_UNIT_CALL("Unit To Unit Call"), UNKNOWN("Unknown"), INVALID("Invalid"); diff --git a/src/main/java/io/github/dsheirer/module/decode/event/CallEventAliasCellRenderer.java b/src/main/java/io/github/dsheirer/module/decode/event/CallEventAliasCellRenderer.java deleted file mode 100644 index 302d08fe..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/CallEventAliasCellRenderer.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.event; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.icon.IconManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; - -/** - * Custom renderer for JTable cells that contain Alias objects. Renders the - * alias name, and if there is an icon name, attempts to get that icon from - * the settings manager and render it. - */ -public class CallEventAliasCellRenderer extends DefaultTableCellRenderer -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(CallEventAliasCellRenderer.class); - - private IconManager mIconManager; - - public CallEventAliasCellRenderer(IconManager iconManager) - { - super(); - mIconManager = iconManager; - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if(value instanceof Alias) - { - Alias alias = (Alias)value; - - label.setText(alias.getName()); - label.setForeground(alias.getDisplayColor()); - label.setIcon(mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE)); - } - else - { - label.setText(""); - label.setForeground(table.getForeground()); - label.setIcon(null); - } - - return label; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/CallEventModel.java b/src/main/java/io/github/dsheirer/module/decode/event/CallEventModel.java deleted file mode 100644 index 93ed0fcb..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/CallEventModel.java +++ /dev/null @@ -1,220 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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.event; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.sample.Listener; - -import javax.swing.table.AbstractTableModel; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; - -public class CallEventModel extends AbstractTableModel implements Listener -{ - private static final long serialVersionUID = 1L; - - private static DecimalFormat mFrequencyFormatter = new DecimalFormat("0.000000"); - - public static final int TIME = 0; - public static final int EVENT = 1; - public static final int FROM_ID = 2; - public static final int FROM_ALIAS = 3; - public static final int TO_ID = 4; - public static final int TO_ALIAS = 5; - public static final int CHANNEL = 6; - public static final int FREQUENCY = 7; - public static final int DETAILS = 8; - - protected int mMaxMessages = 500; - - protected List mEvents = new ArrayList(); - - protected String[] mHeaders = new String[]{"Time", - "Event", - "From", - "Alias", - "To", - "Alias", - "Channel", - "Frequency", - "Details"}; - - private SimpleDateFormat mSDFTime = new SimpleDateFormat("HH:mm:ss"); - - public CallEventModel() - { - } - - public void dispose() - { - mEvents.clear(); - } - - public void reset() - { - mEvents.clear(); - - fireTableDataChanged(); - } - - public int getMaxMessageCount() - { - return mMaxMessages; - } - - public void setMaxMessageCount(int count) - { - mMaxMessages = count; - } - - /** - * Adds, updates or deletes the event from the model. Producers can send - * the same call event multiple times to indicate that information in the - * event is updated. Producers can also mark the event as invalid and the - * event will be removed from the model. - */ - public void receive(final CallEvent event) - { - if(event.isValid()) - { - if(!mEvents.contains(event)) - { - mEvents.add(0, event); - - fireTableRowsInserted(0, 0); - - prune(); - } - else - { - int row = mEvents.indexOf(event); - - fireTableRowsUpdated(row, row); - } - } - else - { - if(mEvents.contains(event)) - { - int row = mEvents.indexOf(event); - - mEvents.remove(event); - - fireTableRowsDeleted(row, row); - } - } - } - - private void prune() - { - while(mEvents.size() > mMaxMessages) - { - int index = mEvents.size() - 1; - mEvents.remove(index); - - fireTableRowsDeleted(index, index); - } - } - - @Override - public int getRowCount() - { - return mEvents.size(); - } - - @Override - public int getColumnCount() - { - return mHeaders.length; - } - - public String getColumnName(int column) - { - return mHeaders[column]; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - synchronized(mEvents) - { - switch(columnIndex) - { - case TIME: - StringBuilder sb = new StringBuilder(); - - sb.append(mSDFTime.format( - mEvents.get(rowIndex).getEventStartTime())); - - if(mEvents.get(rowIndex).getEventEndTime() != 0) - { - sb.append(" - "); - sb.append(mSDFTime.format( - mEvents.get(rowIndex).getEventEndTime())); - } - else if(mEvents.get(rowIndex) - .getCallEventType() == CallEvent.CallEventType.CALL) - { - sb.append(" - In Progress"); - } - - return sb.toString(); - case EVENT: - return mEvents.get(rowIndex).getCallEventType(); - case FROM_ID: - return mEvents.get(rowIndex).getFromID(); - case FROM_ALIAS: - return mEvents.get(rowIndex).getFromIDAlias(); - case TO_ID: - return mEvents.get(rowIndex).getToID(); - case TO_ALIAS: - return mEvents.get(rowIndex).getToIDAlias(); - case CHANNEL: - return mEvents.get(rowIndex).getChannel(); - case FREQUENCY: - long frequency = mEvents.get(rowIndex).getFrequency(); - - if(frequency != 0) - { - return mFrequencyFormatter.format((double) frequency / 1E6d); - } - else - { - return null; - } - case DETAILS: - return mEvents.get(rowIndex).getDetails(); - } - } - - return null; - } - - @Override - public Class getColumnClass(int columnIndex) - { - if(columnIndex == FROM_ALIAS || columnIndex == TO_ALIAS) - { - return Alias.class; - } - - return super.getColumnClass(columnIndex); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/CallEventPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/CallEventPanel.java deleted file mode 100644 index 0223e56b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/CallEventPanel.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.event; - -import io.github.dsheirer.icon.IconManager; -import io.github.dsheirer.module.ProcessingChain; -import io.github.dsheirer.sample.Listener; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; -import java.awt.*; - -public class CallEventPanel extends JPanel implements Listener -{ - private static final long serialVersionUID = 1L; - private JTable mTable; - private CallEventModel mEmptyCallEventModel = new CallEventModel(); - private JScrollPane mEmptyScroller; - private CallEventAliasCellRenderer mRenderer; - - /** - * View for call event table - * @param iconManager to display alias icons in table rows - */ - public CallEventPanel(IconManager iconManager) - { - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - - mTable = new JTable(mEmptyCallEventModel); - mTable.setAutoCreateRowSorter(true); - mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); - - mRenderer = new CallEventAliasCellRenderer(iconManager); - - - mEmptyScroller = new JScrollPane(mTable); - - add(mEmptyScroller); - } - - @Override - public void receive(ProcessingChain processingChain) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mTable.setModel(processingChain != null ? processingChain.getCallEventModel() : mEmptyCallEventModel); - - if(processingChain != null) - { - mTable.getColumnModel().getColumn(CallEventModel.FROM_ALIAS).setCellRenderer(mRenderer); - mTable.getColumnModel().getColumn(CallEventModel.TO_ALIAS).setCellRenderer(mRenderer); - } - } - }); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java new file mode 100644 index 00000000..cfd6e079 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java @@ -0,0 +1,297 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.event; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.protocol.Protocol; + +/** + * Decode event implementation. A decode event is a discrete event such as a call or text message + * or radio registration. + */ +public class DecodeEvent implements IDecodeEvent +{ + private long mTimeStart; + private long mDuration; + private String mEventDescription; + private IdentifierCollection mIdentifierCollection; + private IChannelDescriptor mChannelDescriptor; + private String mDetails; + private Protocol mProtocol; + + public DecodeEvent(long start) + { + mTimeStart = start; + } + + /** + * Creates a new decode event builder with the specified start timestamp. + * @param timeStart for the event + * @return builder + */ + public static DecodeEventBuilder builder(long timeStart) + { + return new DecodeEventBuilder(timeStart); + } + + /** + * Updates the duration value using the end - start timestamps. Note: this method can be + * invoked multiple times and the duration will be set to the value of the final invocation. + * @param timestamp for end of the event. + */ + public void end(long timestamp) + { + if(timestamp > mTimeStart) + { + mDuration = timestamp - mTimeStart; + } + } + + /** + * Updates the current in-progress call with the latest timestamp + */ + public void update(long timestamp) + { + end(timestamp); + } + + /** + * Event start in milliseconds + */ + @Override + public long getTimeStart() + { + return mTimeStart; + } + + /** + * Event duration in milliseconds + */ + @Override + public long getDuration() + { + return mDuration; + } + + /** + * Sets the event duration in milliseconds + */ + public void setDuration(long duration) + { + mDuration = duration; + } + + /** + * Event description + */ + @Override + public String getEventDescription() + { + return mEventDescription; + } + + /** + * Sets the event description text + */ + public void setEventDescription(String description) + { + mEventDescription = description; + } + + /** + * Identifier collection for this event. + */ + @Override + public IdentifierCollection getIdentifierCollection() + { + return mIdentifierCollection; + } + + /** + * Sets the identifier collection for this event. Note: identifier collection should contain + * a FrequencyConfigurationIdentifier and optionally an AliasListConfigurationIdentifier so that + * display elements can be properly configured to display the event. + */ + public void setIdentifierCollection(IdentifierCollection identifierCollection) + { + mIdentifierCollection = identifierCollection; + } + + /** + * Channel descriptor for the channel + */ + @Override + public IChannelDescriptor getChannelDescriptor() + { + return mChannelDescriptor; + } + + /** + * Sets the channel descriptor for the event + */ + public void setChannelDescriptor(IChannelDescriptor channelDescriptor) + { + mChannelDescriptor = channelDescriptor; + } + + /** + * Event details text + */ + @Override + public String getDetails() + { + return mDetails; + } + + /** + * Sets the event details text + */ + public void setDetails(String details) + { + mDetails = details; + } + + /** + * Protocol (ie decoder type) for the event + */ + @Override + public Protocol getProtocol() + { + return mProtocol; + } + + /** + * Sets the protocol for the event + */ + public void setProtocol(Protocol protocol) + { + mProtocol = protocol; + } + + /** + * Builder pattern for constructing decode events. + */ + public static class DecodeEventBuilder + { + protected long mTimeStart; + protected long mDuration; + protected String mEventDescription; + protected IdentifierCollection mIdentifierCollection; + protected IChannelDescriptor mChannelDescriptor; + protected String mDetails; + protected Protocol mProtocol = Protocol.UNKNOWN; + + /** + * Constructs a builder instance with the specified start time in milliseconds + */ + public DecodeEventBuilder(long timeStart) + { + mTimeStart = timeStart; + } + + /** + * Sets the duration value + * @param duration in milliseconds + */ + public DecodeEventBuilder duration(long duration) + { + mDuration = duration; + return this; + } + + /** + * Sets the duration value using the end - start timestamps + * @param timestamp for end of event + */ + public DecodeEventBuilder end(long timestamp) + { + mDuration = timestamp - mTimeStart; + return this; + } + + /** + * Sets the channel descriptor for this event + * @param channelDescriptor + */ + public DecodeEventBuilder channel(IChannelDescriptor channelDescriptor) + { + mChannelDescriptor = channelDescriptor; + return this; + } + + /** + * Sets the event description text + * @param description of the event + */ + public DecodeEventBuilder eventDescription(String description) + { + mEventDescription = description; + return this; + } + + /** + * Sets the identifier collection. + * @param identifierCollection containing optional identifiers like TO, FROM, frequency and + * alias list configuration name. + */ + public DecodeEventBuilder identifiers(IdentifierCollection identifierCollection) + { + mIdentifierCollection = identifierCollection; + return this; + } + + /** + * Sets the details for the event + * @param details + */ + public DecodeEventBuilder details(String details) + { + mDetails = details; + return this; + } + + /** + * Sets the protocol for this event + * @param protocol + */ + public DecodeEventBuilder protocol(Protocol protocol) + { + mProtocol = protocol; + return this; + } + + /** + * Builds the decode event + */ + public DecodeEvent build() + { + DecodeEvent decodeEvent = new DecodeEvent(mTimeStart); + decodeEvent.setChannelDescriptor(mChannelDescriptor); + decodeEvent.setDetails(mDetails); + decodeEvent.setDuration(mDuration); + decodeEvent.setEventDescription(mEventDescription); + decodeEvent.setIdentifierCollection(mIdentifierCollection); + decodeEvent.setProtocol(mProtocol); + return decodeEvent; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java new file mode 100644 index 00000000..917e98be --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 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.event; + +import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class DecodeEventModel extends AbstractTableModel implements Listener +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger(DecodeEventModel.class); + + public static final int COLUMN_TIME = 0; + public static final int COLUMN_DURATION = 1; + public static final int COLUMN_EVENT = 2; + public static final int COLUMN_FROM_ID = 3; + public static final int COLUMN_FROM_ALIAS = 4; + public static final int COLUMN_TO_ID = 5; + public static final int COLUMN_TO_ALIAS = 6; + public static final int COLUMN_CHANNEL = 7; + public static final int COLUMN_FREQUENCY = 8; + public static final int COLUMN_DETAILS = 9; + + protected int mMaxMessages = 500; + + protected List mEvents = new ArrayList<>(); + + protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"}; + + public DecodeEventModel() + { + MyEventBus.getEventBus().register(this); + } + + /** + * Receives preference update notifications via the event bus + * @param preferenceType that was updated + */ + @Subscribe + public void preferenceUpdated(PreferenceType preferenceType) + { + if(preferenceType == PreferenceType.DECODE_EVENT) + { + for(int row = 0; row < mEvents.size(); row++) + { + fireTableCellUpdated(row, COLUMN_TIME); + } + } + else if(preferenceType == PreferenceType.IDENTIFIER) + { + for(int row = 0; row < mEvents.size(); row++) + { + fireTableCellUpdated(row, COLUMN_FROM_ID); + fireTableCellUpdated(row, COLUMN_TO_ID); + } + } + } + + + public void dispose() + { + MyEventBus.getEventBus().unregister(this); + Iterator it = mEvents.iterator(); + + while(it.hasNext()) + { + it.remove(); + } + } + + public void reset() + { + dispose(); + fireTableDataChanged(); + } + + public int getMaxMessageCount() + { + return mMaxMessages; + } + + public void setMaxMessageCount(int count) + { + mMaxMessages = count; + } + + /** + * Adds, updates or deletes the event from the model. Producers can send + * the same call event multiple times to indicate that information in the + * event is updated. Producers can also mark the event as invalid and the + * event will be removed from the model. + */ + public void receive(final IDecodeEvent event) + { + if(!mEvents.contains(event)) + { + mEvents.add(0, event); + fireTableRowsInserted(0, 0); + prune(); + } + else + { + int row = mEvents.indexOf(event); + fireTableRowsUpdated(row, row); + } + } + + private void prune() + { + while(mEvents.size() > mMaxMessages) + { + int index = mEvents.size() - 1; + mEvents.remove(index); + fireTableRowsDeleted(index, index); + } + } + + @Override + public int getRowCount() + { + return mEvents.size(); + } + + @Override + public int getColumnCount() + { + return mHeaders.length; + } + + public String getColumnName(int column) + { + return mHeaders[column]; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) + { + synchronized(mEvents) + { + IDecodeEvent event = mEvents.get(rowIndex); + + if(event != null) + { + switch(columnIndex) + { + case COLUMN_TIME: + return event.getTimeStart(); + case COLUMN_DURATION: + return event.getDuration(); + case COLUMN_EVENT: + return event.getEventDescription(); + case COLUMN_FROM_ID: + return event.getIdentifierCollection(); + case COLUMN_FROM_ALIAS: + return event.getIdentifierCollection(); + case COLUMN_TO_ID: + return event.getIdentifierCollection(); + case COLUMN_TO_ALIAS: + return event.getIdentifierCollection(); + case COLUMN_CHANNEL: + return event.getChannelDescriptor(); + case COLUMN_FREQUENCY: + return event.getChannelDescriptor(); + case COLUMN_DETAILS: + return event.getDetails(); + } + } + } + + return null; + } + + @Override + public Class getColumnClass(int columnIndex) + { + switch(columnIndex) + { + case COLUMN_DURATION: + case COLUMN_TIME: + return Long.class; + case COLUMN_DETAILS: + case COLUMN_EVENT: + return String.class; + case COLUMN_FREQUENCY: + case COLUMN_FROM_ALIAS: + case COLUMN_FROM_ID: + case COLUMN_TO_ALIAS: + case COLUMN_TO_ID: + return IdentifierCollection.class; + case COLUMN_CHANNEL: + return IChannelDescriptor.class; + } + + return super.getColumnClass(columnIndex); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java new file mode 100644 index 00000000..057b7374 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java @@ -0,0 +1,411 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.event; + +import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.module.ProcessingChain; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.sample.Listener; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.Color; +import java.awt.Component; +import java.awt.EventQueue; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class DecodeEventPanel extends JPanel implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger(DecodeEventPanel.class); + + private static final long serialVersionUID = 1L; + private JTable mTable; + private DecodeEventModel mEmptyDecodeEventModel; + private JScrollPane mEmptyScroller; + private IconManager mIconManager; + private AliasModel mAliasModel; + private UserPreferences mUserPreferences; + private TimestampCellRenderer mTimestampCellRenderer; + + /** + * View for call event table + * @param iconManager to display alias icons in table rows + */ + public DecodeEventPanel(IconManager iconManager, UserPreferences userPreferences, AliasModel aliasModel) + { + MyEventBus.getEventBus().register(this); + + setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); + mIconManager = iconManager; + mAliasModel = aliasModel; + mUserPreferences = userPreferences; + mTimestampCellRenderer = new TimestampCellRenderer(); + mEmptyDecodeEventModel = new DecodeEventModel(); + mTable = new JTable(mEmptyDecodeEventModel); + mTable.setAutoCreateRowSorter(true); + mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + updateCellRenderers(); + + mEmptyScroller = new JScrollPane(mTable); + add(mEmptyScroller); + } + + /** + * Receives preference update notifications via the event bus + * @param preferenceType that was updated + */ + @Subscribe + public void preferenceUpdated(PreferenceType preferenceType) + { + if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.IDENTIFIER) + { + EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences()); + } + } + + + private void updateCellRenderers() + { + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME) + .setCellRenderer(mTimestampCellRenderer); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION) + .setCellRenderer(new DurationCellRenderer()); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID) + .setCellRenderer(new IdentifierCellRenderer(Role.FROM)); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS) + .setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM)); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID) + .setCellRenderer(new IdentifierCellRenderer(Role.TO)); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS) + .setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO)); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL) + .setCellRenderer(new ChannelDescriptorCellRenderer()); + mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY) + .setCellRenderer(new FrequencyCellRenderer()); + } + + @Override + public void receive(ProcessingChain processingChain) + { + EventQueue.invokeLater(() -> { + mTable.setModel(processingChain != null ? processingChain.getDecodeEventModel() : mEmptyDecodeEventModel); + + if(processingChain != null) + { + updateCellRenderers(); + } + }); + } + + /** + * Custom cell renderer for displaying identifiers from an identifier collection + */ + public class IdentifierCellRenderer extends DefaultTableCellRenderer + { + protected Role mRole; + + /** + * Constructs an instance of the cell renderer. + * + * @param role of the identifier + */ + public IdentifierCellRenderer(Role role) + { + mRole = role; + setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if(value instanceof IdentifierCollection) + { + List identifiers = ((IdentifierCollection)value).getIdentifiers(mRole); + +// if(identifiers.size() > 1) +// { +// StringBuilder sb = new StringBuilder(); +// sb.append("Multiple Identifiers:\n"); +// for(Identifier identifier: identifiers) +// { +// sb.append("ID:").append(identifier.debug()).append("\n"); +// } +// mLog.debug(sb.toString()); +// } + label.setText(format(identifiers)); + } + else + { + label.setText(null); + } + + return label; + } + + /** + * Formats a list of identifiers as a comma separated list of values + * @param identifiers to format + * @return formatted list or null + */ + protected String format(List identifiers) + { + if(identifiers == null || identifiers.isEmpty()) + { + return null; + } + + StringBuilder sb = new StringBuilder(); + + for(Identifier identifier: identifiers) + { + if(sb.length() > 0) + { + sb.append(","); + } + + sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier)); + } + + return sb.toString(); + } + } + + /** + * Cell renderer for identifier aliases + */ + public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer + { + private Role mRole; + + /** + * Constructs an instance of the cell renderer. + * + * @param role of the identifier + */ + public AliasedIdentifierCellRenderer(Role role) + { + mRole = role; + setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + Color color = mTable.getForeground(); + ImageIcon icon = null; + String text = null; + + if(value instanceof IdentifierCollection) + { + IdentifierCollection identifierCollection = (IdentifierCollection)value; + List identifiers = identifierCollection.getIdentifiers(mRole); + + if(identifiers != null && !identifiers.isEmpty()) + { + AliasList aliasList = mAliasModel.getAliasList(identifierCollection); + + if(aliasList != null) + { + StringBuilder sb = new StringBuilder(); + + for(Identifier identifier: identifiers) + { + Alias alias = aliasList.getAlias(identifier); + + if(alias != null) + { + if(sb.length() > 0) + { + sb.append(","); + } + sb.append(alias.getName()); + + color = alias.getDisplayColor(); + icon = mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE); + } + } + + text = sb.toString(); + } + } + } + + label.setText(text); + label.setForeground(color); + label.setIcon(icon); + + return label; + } + } + + public class TimestampCellRenderer extends DefaultTableCellRenderer + { + private SimpleDateFormat mTimestampFormatter; + + public TimestampCellRenderer() + { + setHorizontalAlignment(JLabel.CENTER); + updatePreferences(); + } + + public void updatePreferences() + { + mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if(value instanceof Long) + { + label.setText(mTimestampFormatter.format(new Date((long)value))); + } + else + { + label.setText(null); + } + + return label; + } + } + + public class DurationCellRenderer extends DefaultTableCellRenderer + { + private DecimalFormat mDecimalFormat = new DecimalFormat("0.0"); + + public DurationCellRenderer() + { + setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + String formatted = null; + + if(value instanceof Long) + { + long duration = (long)value; + + if(duration > 0) + { + formatted = mDecimalFormat.format((double)duration / 1e3d); + } + } + + label.setText(formatted); + + return label; + } + } + + /** + * Frequency value cell renderer + */ + public class FrequencyCellRenderer extends DefaultTableCellRenderer + { + private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000 MHz"); + + public FrequencyCellRenderer() + { + setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + String formatted = null; + + if(value instanceof IChannelDescriptor) + { + IChannelDescriptor channelDescriptor = (IChannelDescriptor)value; + + long frequency = channelDescriptor.getDownlinkFrequency(); + + if(frequency > 0) + { + formatted = mFrequencyFormatter.format(frequency / 1e6d); + } + } + + label.setText(formatted); + + return label; + } + } + + /** + * Channel descriptor value cell renderer + */ + public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer + { + public ChannelDescriptorCellRenderer() + { + setHorizontalAlignment(JLabel.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if(value instanceof IChannelDescriptor) + { + label.setText(value.toString()); + } + else + { + label.setText(null); + } + + return label; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java new file mode 100644 index 00000000..e2cfabc7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java @@ -0,0 +1,85 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.event; + +public enum DecodeEventType +{ + ANNOUNCEMENT("Announcement"), + AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"), + CALL("Call"), + CALL_ENCRYPTED("Encrypted Call"), + CALL_GROUP("Group Call"), + CALL_GROUP_ENCRYPTED("Encrypted Group Call"), + CALL_PATCH_GROUP("Patch Call"), + CALL_PATCH_GROUP_ENCRYPTED("Encrypted Patch Call"), + CALL_ALERT("Call Alert"), + CALL_DETECT("Call Detect"), + CALL_DO_NOT_MONITOR("Call-Do Not Monitor"), + CALL_END("Call End"), + CALL_INTERCONNECT("Telephone Call"), + CALL_INTERCONNECT_ENCRYPTED("Encrypted Telephone Call"), + CALL_UNIQUE_ID("UID Call"), + CALL_UNIT_TO_UNIT("Unit To Unit Call"), + CALL_UNIT_TO_UNIT_ENCRYPTED("Encrypted Unit To Unit Call"), + CALL_NO_TUNER("Call - No Tuner"), + CALL_TIMEOUT("Call Timeout"), + COMMAND("Command"), + DATA_CALL("Data Call"), + DATA_CALL_ENCRYPTED("Encrypted Data Call"), + DATA_PACKET("Data Packet"), + DEREGISTER("Deregister"), + EMERGENCY("EMERGENCY"), + FUNCTION("Function"), + GPS("GPS"), + ID_ANI("ANI"), + ID_UNIQUE("Unique ID"), + IP_PACKET("IP Packet"), + NOTIFICATION("Notification"), + PAGE("Page"), + QUERY("Query"), + REGISTER("Register"), + REGISTER_ESN("ESN"), + REQUEST("Request"), + RESPONSE("Response"), + RESPONSE_PACKET("Response Packet"), + SDM("Short Data Message"), + STATION_ID("Station ID"), + STATUS("Status"), + UDP_PACKET("UDP/IP Packet"), + UNKNOWN("Unknown"); + + private String mLabel; + + DecodeEventType(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return mLabel; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/ICallEventListener.java b/src/main/java/io/github/dsheirer/module/decode/event/ICallEventListener.java deleted file mode 100644 index 36d52790..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/ICallEventListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.dsheirer.module.decode.event; - -import io.github.dsheirer.sample.Listener; - -public interface ICallEventListener -{ - public Listener getCallEventListener(); -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/ICallEventProvider.java b/src/main/java/io/github/dsheirer/module/decode/event/ICallEventProvider.java deleted file mode 100644 index bc8fc786..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/ICallEventProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.dsheirer.module.decode.event; - -import io.github.dsheirer.sample.Listener; - -public interface ICallEventProvider -{ - public void addCallEventListener( Listener listener ); - public void removeCallEventListener( Listener listener ); -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java new file mode 100644 index 00000000..722c72da --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java @@ -0,0 +1,74 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.event; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.protocol.Protocol; + +/** + * Decode event interface + */ +public interface IDecodeEvent +{ + /** + * Event start time + * + * @return timestamp in milliseconds + */ + long getTimeStart(); + + /** + * Event duration. + * + * @return event duration in milliseconds or 0 if there is no duration + */ + long getDuration(); + + /** + * A description of the event or event type + */ + String getEventDescription(); + + /** + * Collection of identifiers associated with the event. This collection should contain a + * Role.FROM and a Role.TO identifier, a Decoder Type identifier, and (optionally) an Alias List + * Configuration identifier. + */ + IdentifierCollection getIdentifierCollection(); + + /** + * Channel descriptor for the channel where the event occurred + * + * @return descriptor or null + */ + IChannelDescriptor getChannelDescriptor(); + + /** + * Optional Details about the event + */ + String getDetails(); + + /** + * Protocol for the decoder that produced the event + */ + Protocol getProtocol(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventListener.java b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventListener.java new file mode 100644 index 00000000..9c53fca2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventListener.java @@ -0,0 +1,28 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.event; + +import io.github.dsheirer.sample.Listener; + +public interface IDecodeEventListener +{ + Listener getDecodeEventListener(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventProvider.java b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventProvider.java new file mode 100644 index 00000000..8c10f5c7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEventProvider.java @@ -0,0 +1,9 @@ +package io.github.dsheirer.module.decode.event; + +import io.github.dsheirer.sample.Listener; + +public interface IDecodeEventProvider +{ + void addDecodeEventListener(Listener listener ); + void removeDecodeEventListener(Listener listener ); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java index d4ff0809..25a10e08 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java @@ -1,193 +1,180 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014-2016 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.event; import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.sample.Listener; import javax.swing.table.AbstractTableModel; -import java.awt.*; +import java.awt.EventQueue; import java.text.SimpleDateFormat; -import java.util.Date; import java.util.LinkedList; -public class MessageActivityModel extends AbstractTableModel - implements Listener +public class MessageActivityModel extends AbstractTableModel implements Listener { private static final long serialVersionUID = 1L; private static final int TIME = 0; private static final int PROTOCOL = 1; - private static final int ERROR_STATUS = 2; - private static final int MESSAGE = 3; - private static final int MESSAGE_BITS = 4; + private static final int MESSAGE = 2; - protected int mMaxMessages = 500; - protected LinkedList mMessages = new LinkedList(); + protected int mMaxMessages = 500; + protected LinkedList mMessageItems = new LinkedList<>(); + protected int[] mColumnWidths = {20, 20, 500}; + protected String[] mHeaders = new String[]{"Time", "Protocol", "Message"}; - protected int[] mColumnWidths = { 110, 110, 110, -1, -1 }; + private SimpleDateFormat mSDFTime = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); - protected String[] mHeaders = new String[] { "Time", - "Protocol", - "Error Check", - "Message", - "Binary" }; + private FilterSet mMessageFilter; - private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" ); - - private boolean mNewMessagesFirst = true; - - private FilterSet mMessageFilter; - - public MessageActivityModel( FilterSet messageFilter ) - { - mMessageFilter = messageFilter; - } - - public MessageActivityModel() - { - } - - /** - * Clears all messages from history - */ - public void clear() - { - EventQueue.invokeLater( new Runnable() - { - @Override + public MessageActivityModel(FilterSet messageFilter) + { + mMessageFilter = messageFilter; + } + + public MessageActivityModel() + { + } + + /** + * Clears all messages from history + */ + public void clear() + { + EventQueue.invokeLater(new Runnable() + { + @Override public void run() { - int messageCount = mMessages.size(); - - mMessages.clear(); + int messageCount = mMessageItems.size(); - fireTableRowsDeleted( 0, messageCount - 1 ); + mMessageItems.clear(); + + fireTableRowsDeleted(0, messageCount - 1); } - }); - } - - public FilterSet getMessageFilter() - { - return mMessageFilter; - } - - public void dispose() - { - mMessages.clear(); - } - - public int[] getColumnWidths() - { - return mColumnWidths; - } - - public void setColumnWidths( int[] widths ) - { - if( widths.length != 5 ) - { - throw new IllegalArgumentException( "MessageActivityModel - " - + "column widths array should have 5 elements" ); - } - else - { - mColumnWidths = widths; - } - } - - public int getMaxMessageCount() - { - return mMaxMessages; - } + }); + } - public void setMaxMessageCount( int count ) - { - mMaxMessages = count; - } - - public void receive( final Message message ) - { - if( message.isValid() && mMessageFilter.passes( message ) ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - mMessages.addFirst( message ); + public FilterSet getMessageFilter() + { + return mMessageFilter; + } - MessageActivityModel.this.fireTableRowsInserted( 0, 0 ); + public void dispose() + { + mMessageItems.clear(); + } - prune(); - } - } ); - } - } - - private void prune() - { - while( mMessages.size() > mMaxMessages ) - { - mMessages.removeLast(); - - super.fireTableRowsDeleted( mMessages.size() - 1, mMessages.size() - 1 ); - } - } + public int[] getColumnWidths() + { + return mColumnWidths; + } - @Override + public void setColumnWidths(int[] widths) + { + if(widths.length != 3) + { + throw new IllegalArgumentException("MessageActivityModel - column widths array should have 3 elements"); + } + else + { + mColumnWidths = widths; + } + } + + public int getMaxMessageCount() + { + return mMaxMessages; + } + + public void setMaxMessageCount(int count) + { + mMaxMessages = count; + } + + public void receive(final IMessage message) + { + if(mMessageFilter.passes(message)) + { + final MessageItem messageItem = new MessageItem(message); + + EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + mMessageItems.addFirst(messageItem); + + MessageActivityModel.this.fireTableRowsInserted(0, 0); + + prune(); + } + }); + } + } + + private void prune() + { + while(mMessageItems.size() > mMaxMessages) + { + MessageItem removed = mMessageItems.removeLast(); + removed.dispose(); + super.fireTableRowsDeleted(mMessageItems.size() - 1, mMessageItems.size() - 1); + } + } + + @Override public int getRowCount() { - return mMessages.size(); + return mMessageItems.size(); } - @Override + @Override public int getColumnCount() { - return mHeaders.length; + return mHeaders.length; } - public String getColumnName( int column ) - { - return mHeaders[ column ]; - } - - @Override - public Object getValueAt( int rowIndex, int columnIndex ) + public String getColumnName(int column) { - if( 0 <= rowIndex && rowIndex < mMessages.size() ) - { - Message message = mMessages.get( rowIndex ); - - switch( columnIndex ) - { - case TIME: - return mSDFTime.format( new Date( message.getTimeReceived() ) ); - case PROTOCOL: - return message.getProtocol(); - case ERROR_STATUS: - return message.getErrorStatus(); - case MESSAGE: - return message.getMessage(); - case MESSAGE_BITS: - return message.getBinaryMessage(); - } - } - - return null; + return mHeaders[column]; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) + { + if(0 <= rowIndex && rowIndex < mMessageItems.size()) + { + MessageItem messageItem = mMessageItems.get(rowIndex); + + switch(columnIndex) + { + case TIME: + return messageItem.getTimestamp(mSDFTime); + case PROTOCOL: + return messageItem.getProtocol(); + case MESSAGE: + return messageItem.getText(); + default: + break; + } + } + + return null; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java index fd65789a..42c89762 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java @@ -23,7 +23,7 @@ import com.jidesoft.swing.JideSplitButton; import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.filter.FilterEditorPanel; import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.sample.Listener; import net.miginfocom.swing.MigLayout; @@ -81,6 +81,10 @@ public class MessageActivityPanel extends JPanel implements Listener filter = (FilterSet) ((MessageActivityModel)mTable.getModel()).getMessageFilter(); + FilterSet filter = (FilterSet) ((MessageActivityModel) mTable.getModel()) + .getMessageFilter(); - FilterEditorPanel panel = new FilterEditorPanel(filter); + FilterEditorPanel panel = new FilterEditorPanel<>(filter); JScrollPane scroller = new JScrollPane(panel); scroller.setViewportView(panel); diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageItem.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageItem.java new file mode 100644 index 00000000..df669c9b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageItem.java @@ -0,0 +1,97 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.event; + +import io.github.dsheirer.message.IMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Simple message carrier class that persists activity model column values so that they do not need + * to be recalculated with each row update. + */ +public class MessageItem +{ + private final static Logger mLog = LoggerFactory.getLogger(MessageItem.class); + private IMessage mMessage; + private String mTimestamp; + private String mProtocol; + private String mText; + + public MessageItem(IMessage message) + { + mMessage = message; + } + + public IMessage getMessage() + { + return mMessage; + } + + public void dispose() + { + mMessage = null; + mTimestamp = null; + mProtocol = null; + mText = null; + } + + public String getTimestamp(SimpleDateFormat simpleDateFormat) + { + if(mTimestamp == null) + { + mTimestamp = simpleDateFormat.format(new Date(getMessage().getTimestamp())); + } + + return mTimestamp; + } + + public String getText() + { + if(mText == null) + { + try + { + mText = getMessage().toString(); + } + catch(Throwable t) + { + mLog.error("Error accessing message toString() method - protocol [" + getProtocol() + "]", t); + mText = "MESSAGE ITEM ENCOUNTERED PARSING ERROR"; + } + } + + return mText; + } + + public String getProtocol() + { + if(mProtocol == null) + { + mProtocol = getMessage().getProtocol().toString(); + } + + return mProtocol; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java new file mode 100644 index 00000000..87fca123 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java @@ -0,0 +1,239 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.event; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.protocol.Protocol; +import org.jdesktop.swingx.mapviewer.GeoPosition; + +public class PlottableDecodeEvent extends DecodeEvent +{ + private GeoPosition mGeoPosition; + private double mHeading; + private double mSpeed; + + public PlottableDecodeEvent(long start) + { + super(start); + } + + /** + * Creates a new decode event builder with the specified start timestamp. + * @param timeStart for the event + * @return builder + */ + public static PlottableDecodeEventBuilder plottableBuilder(long timeStart) + { + return new PlottableDecodeEventBuilder(timeStart); + } + + /** + * Sets the location for the event + */ + public void setLocation(GeoPosition geoPosition) + { + mGeoPosition = geoPosition; + } + + /** + * Location of the event + */ + public GeoPosition getLocation() + { + return mGeoPosition; + } + + /** + * Sets the heading for the mobile plottable event + * @param heading + */ + public void setHeading(double heading) + { + mHeading = heading; + } + + /** + * Heading of the mobile plottable event + */ + public double getHeading() + { + return mHeading; + } + + /** + * Sets the speed of the mobile plottable event in kph + */ + public void setSpeed(double speed) + { + mSpeed = speed; + } + + /** + * Speed of the mobile plottable event in kph + */ + public double getSpeed() + { + return mSpeed; + } + + /** + * Builder pattern for constructing decode events. + */ + public static class PlottableDecodeEventBuilder + { + private long mTimeStart; + private long mDuration; + private String mEventDescription; + private IdentifierCollection mIdentifierCollection; + private IChannelDescriptor mChannelDescriptor; + private String mDetails; + private Protocol mProtocol = Protocol.UNKNOWN; + private GeoPosition mGeoPosition; + private double mHeading; + private double mSpeed; + + /** + * Constructs a builder instance with the specified start time in milliseconds + */ + public PlottableDecodeEventBuilder(long timeStart) + { + mTimeStart = timeStart; + } + + /** + * Sets the duration value + * @param duration in milliseconds + */ + public PlottableDecodeEventBuilder duration(long duration) + { + mDuration = duration; + return this; + } + + /** + * Sets the duration value using the end - start timestamps + * @param timestamp for end of event + */ + public PlottableDecodeEventBuilder end(long timestamp) + { + mDuration = timestamp - mTimeStart; + return this; + } + + /** + * Sets the channel descriptor for this event + * @param channelDescriptor + */ + public PlottableDecodeEventBuilder channel(IChannelDescriptor channelDescriptor) + { + mChannelDescriptor = channelDescriptor; + return this; + } + + /** + * Sets the event description text + * @param description of the event + */ + public PlottableDecodeEventBuilder eventDescription(String description) + { + mEventDescription = description; + return this; + } + + /** + * Sets the identifier collection. + * @param identifierCollection containing optional identifiers like TO, FROM, frequency and + * alias list configuration name. + */ + public PlottableDecodeEventBuilder identifiers(IdentifierCollection identifierCollection) + { + mIdentifierCollection = identifierCollection; + return this; + } + + /** + * Sets the details for the event + * @param details + */ + public PlottableDecodeEventBuilder details(String details) + { + mDetails = details; + return this; + } + + /** + * Sets the protocol for this event + * @param protocol + */ + public PlottableDecodeEventBuilder protocol(Protocol protocol) + { + mProtocol = protocol; + return this; + } + + /** + * Sets the location for the plottable event + */ + public PlottableDecodeEventBuilder location(GeoPosition geoPosition) + { + mGeoPosition = geoPosition; + return this; + } + + /** + * Sets the speed for the plottable event in kph + */ + public PlottableDecodeEventBuilder speed(double speed) + { + mSpeed = speed; + return this; + } + + /** + * Sets the heading for the plottable event in degrees relative to true North + */ + public PlottableDecodeEventBuilder heading(double heading) + { + mHeading = heading; + return this; + } + + /** + * Builds the decode event + */ + public PlottableDecodeEvent build() + { + PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart); + decodeEvent.setChannelDescriptor(mChannelDescriptor); + decodeEvent.setDetails(mDetails); + decodeEvent.setDuration(mDuration); + decodeEvent.setEventDescription(mEventDescription); + decodeEvent.setIdentifierCollection(mIdentifierCollection); + decodeEvent.setProtocol(mProtocol); + decodeEvent.setLocation(mGeoPosition); + decodeEvent.setHeading(mHeading); + decodeEvent.setSpeed(mSpeed); + return decodeEvent; + } + } + +} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1Message.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1Message.java deleted file mode 100644 index 14bccd61..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1Message.java +++ /dev/null @@ -1,201 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.fleetsync1; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.map.Plottable; -import io.github.dsheirer.message.Message; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class Fleetsync1Message extends Message -{ - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); - - private BinaryMessage mMessage; - - public Fleetsync1Message( BinaryMessage message ) - { - mMessage = message; - } - - public boolean isValid() - { - return true; - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) ); - sb.append( " " ); - sb.append( "FS1 [" + mMessage.toString() + "]" ); - - return sb.toString(); - } - - /** - * Pads spaces onto the end of the value to make it 'places' long - */ - public String pad( String value, int places, String padCharacter ) - { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); - } - - /** - * Pads an integer value with additional zeroes to make it decimalPlaces long - */ - public String format( int number, int decimalPlaces ) - { - StringBuilder sb = new StringBuilder(); - - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); - - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - @SuppressWarnings( "unused" ) - private int getInt( int[] bits ) - { - int retVal = 0; - - for( int x = 0; x < bits.length; x++ ) - { - if( mMessage.get( bits[ x ] ) ) - { - retVal += 1< getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - return aliases; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1MessageProcessor.java deleted file mode 100644 index 5d85f070..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync1/Fleetsync1MessageProcessor.java +++ /dev/null @@ -1,51 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.fleetsync1; - -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.sample.Broadcaster; -import io.github.dsheirer.sample.Listener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Fleetsync1MessageProcessor implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( Fleetsync1MessageProcessor.class ); - - private Broadcaster mBroadcaster = new Broadcaster(); - - @Override - public void receive( BinaryMessage buffer ) - { - Fleetsync1Message message = new Fleetsync1Message( buffer ); - - mBroadcaster.receive( message ); - } - - public void addMessageListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeMessageListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java index 5d5676d7..6554739f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -1,23 +1,24 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * 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.fleetsync2; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.bits.SyncPattern; import io.github.dsheirer.dsp.afsk.AFSK1200Decoder; @@ -36,34 +37,32 @@ public class Fleetsync2Decoder extends AbstractAFSKDecoder /** * Constructs a decoder for Fleetsync II protocol + * * @param afsk1200Decoder to use for decoding the symbols - * @param aliasList to assign aliases to decoded values */ - protected Fleetsync2Decoder(AFSK1200Decoder afsk1200Decoder, AliasList aliasList) + protected Fleetsync2Decoder(AFSK1200Decoder afsk1200Decoder) { super(afsk1200Decoder); - init(aliasList); + init(); } /** * Constructs a decoder for Fleetsync II protocol - * @param aliasList to assign aliases to decoded values */ - public Fleetsync2Decoder(AliasList aliasList) + public Fleetsync2Decoder() { super(AFSK1200Decoder.Output.NORMAL); - init(aliasList); + init(); } /** * Initializes the decoding chain. - * @param aliasList to assign to the message processor */ - private void init(AliasList aliasList) + private void init() { mMessageFramer = new MessageFramer(SyncPattern.FLEETSYNC2.getPattern(), MESSAGE_LENGTH); getDecoder().setSymbolProcessor(mMessageFramer); - mMessageProcessor = new Fleetsync2MessageProcessor(aliasList); + mMessageProcessor = new Fleetsync2MessageProcessor(); mMessageFramer.addMessageListener(mMessageProcessor); mMessageProcessor.setMessageListener(getMessageListener()); } diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderInstrumented.java index 894d22c7..f05dea5c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderInstrumented.java @@ -29,7 +29,7 @@ public class Fleetsync2DecoderInstrumented extends Fleetsync2Decoder implements public Fleetsync2DecoderInstrumented() { - super(new AFSK1200DecoderInstrumented(false), null); + super(new AFSK1200DecoderInstrumented(false)); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java index e65848d3..a822230b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java @@ -18,42 +18,30 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.fleetsync2; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; -import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.util.StringUtils; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.DecodeEventType; +import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; +import io.github.dsheirer.module.decode.fleetsync2.identifier.FleetsyncIdentifier; +import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message; +import io.github.dsheirer.protocol.Protocol; import java.util.Iterator; +import java.util.Set; import java.util.TreeSet; public class Fleetsync2DecoderState extends DecoderState { - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); + private Set mIdents = new TreeSet<>(); - private AliasedStringAttributeMonitor mFromAttribute; - private AliasedStringAttributeMonitor mToAttribute; - private String mMessage; - private String mMessageType; - private long mFrequency; - - public Fleetsync2DecoderState(AliasList aliasList) + public Fleetsync2DecoderState() { - super(aliasList); - - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.FLEETSYNC); - mToAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.FLEETSYNC); + super(); } @Override @@ -62,12 +50,6 @@ public class Fleetsync2DecoderState extends DecoderState return DecoderType.FLEETSYNC2; } - @Override - public void dispose() - { - super.dispose(); - } - /** * Resets the overall decoder state and clears any accumulated event details */ @@ -75,21 +57,16 @@ public class Fleetsync2DecoderState extends DecoderState public void reset() { mIdents.clear(); - mEmergencyIdents.clear(); - - resetState(); } @Override public void start() { - } @Override public void stop() { - } @Override @@ -98,110 +75,60 @@ public class Fleetsync2DecoderState extends DecoderState } - /** - * Resets this decoder state - */ - public void resetState() - { - mFromAttribute.reset(); - mToAttribute.reset(); - mMessage = null; - mMessageType = null; - } - @Override - public void receive(Message message) + public void receive(IMessage message) { - if(message instanceof FleetsyncMessage) + if(message.isValid() && message instanceof Fleetsync2Message) { - FleetsyncMessage fleetsync = (FleetsyncMessage) message; + Fleetsync2Message fleetsync = (Fleetsync2Message)message; - if(fleetsync.isValid()) + getIdentifierCollection().update(fleetsync.getIdentifiers()); + + mIdents.add(fleetsync.getFromIdentifier()); + mIdents.add(fleetsync.getToIdentifier()); + + switch(fleetsync.getMessageType()) { - State state = State.CALL; + case ANI: + case EMERGENCY: + case LONE_WORKER_EMERGENCY: + DecodeEvent aniEvent = DecodeEvent.builder(fleetsync.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(fleetsync.getMessageType().toString()) + .details(fleetsync.toString()) + .identifiers(getIdentifierCollection().copyOf()) + .build(); - mFromAttribute.process(fleetsync.getFromID()); - mIdents.add(fleetsync.getFromID()); - - FleetsyncMessageType type = fleetsync.getMessageType(); - - if(type != FleetsyncMessageType.ANI) - { - mToAttribute.process(fleetsync.getToID()); - mIdents.add(fleetsync.getToID()); - } - - setMessageType(type.getLabel()); - - switch(type) - { - case GPS: - setMessage(fleetsync.getGPSLocation()); - state = State.DATA; - break; - case STATUS: - StringBuilder sb = new StringBuilder(); - sb.append(fleetsync.getStatus()); - - Alias status = fleetsync.getStatusAlias(); - - if(status != null) - { - sb.append("/"); - sb.append(status.getName()); - } - - setMessage(sb.toString()); - state = State.DATA; - break; - case EMERGENCY: - case LONE_WORKER_EMERGENCY: - mEmergencyIdents.add(fleetsync.getFromID()); - state = State.DATA; - break; - default: - break; - } - - FleetsyncCallEvent fsCallEvent = - FleetsyncCallEvent.getFleetsync2Event(fleetsync, mFrequency); - - fsCallEvent.setAliasList(getAliasList()); - - broadcast(fsCallEvent); - - /* Broadcast decode event so that the channel state will - * kick in and reset everything after a short delay */ - broadcast(new DecoderStateEvent(this, Event.DECODE, state)); + broadcast(aniEvent); + broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.CALL)); + break; + case ACKNOWLEDGE: + case PAGING: + case STATUS: + case UNKNOWN: + DecodeEvent statusEvent = DecodeEvent.builder(fleetsync.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(fleetsync.getMessageType().toString()) + .details(fleetsync.toString()) + .identifiers(getIdentifierCollection().copyOf()) + .build(); + broadcast(statusEvent); + broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA)); + break; + case GPS: + PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.GPS.toString()) + .details(fleetsync.toString()) + .identifiers(getIdentifierCollection().copyOf()) + .protocol(Protocol.FLEETSYNC) + .build(); + broadcast(plottableDecodeEvent); + broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA)); + break; } - } - } - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, mMessage)); - } - } - - public String getMessageType() - { - return "Fleetsync " + mMessageType; - } - - public void setMessageType(String type) - { - if(!StringUtils.isEqual(mMessageType, type)) - { - mMessageType = type; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, getMessageType())); + getIdentifierCollection().remove(IdentifierClass.USER); } } @@ -214,10 +141,7 @@ public class Fleetsync2DecoderState extends DecoderState switch(event.getEvent()) { case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); + reset(); break; default: break; @@ -239,80 +163,14 @@ public class Fleetsync2DecoderState extends DecoderState } else { - Iterator it = mIdents.iterator(); + Iterator it = mIdents.iterator(); while(it.hasNext()) { - String ident = it.next(); - - sb.append(" "); - sb.append(formatIdent(ident)); - - if(hasAliasList()) - { - Alias alias = getAliasList().getFleetsyncAlias(ident); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nFleetsync Emergency Activations\n"); - - if(mEmergencyIdents.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while(it.hasNext()) - { - String ident = it.next(); - - sb.append(" "); - sb.append(formatIdent(ident)); - - if(hasAliasList()) - { - Alias alias = getAliasList().getFleetsyncAlias(ident); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - - sb.append("\n"); - } + sb.append(" ").append(it.next().formatted()).append("\n"); } } return sb.toString(); } - - public static String formatIdent(String ident) - { - StringBuilder sb = new StringBuilder(); - - if(ident.length() == 7) - { - sb.append(ident.substring(0, 3)); - sb.append("-"); - sb.append(ident.substring(3, 7)); - - return sb.toString(); - } - else - { - return ident; - } - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2MessageProcessor.java index c958b785..3cad7928 100644 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2MessageProcessor.java @@ -1,62 +1,97 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.fleetsync2; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.fleetsync2.message.AutomaticNumberIdentification; +import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message; +import io.github.dsheirer.module.decode.fleetsync2.message.LocationReport; +import io.github.dsheirer.module.decode.fleetsync2.message.Status; import io.github.dsheirer.sample.Listener; -public class Fleetsync2MessageProcessor implements Listener +/** + * Fleetsync message processor converts binary messages into IMessage implementations. + */ +public class Fleetsync2MessageProcessor implements Listener { - private Listener mMessageListener; - - private AliasList mAliasList; - - public Fleetsync2MessageProcessor( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mMessageListener = null; - mAliasList = null; - } - - @Override - public void receive( BinaryMessage buffer ) - { - FleetsyncMessage message = new FleetsyncMessage( buffer, mAliasList ); + private Listener mMessageListener; - if( mMessageListener != null ) - { - mMessageListener.receive( message ); - } - } - - public void setMessageListener( Listener listener ) + /** + * Constructs a message processor + */ + public Fleetsync2MessageProcessor() { - mMessageListener = listener; } + public void dispose() + { + mMessageListener = null; + } + + /** + * Primary receive interface for demodulated binary messages + */ + @Override + public void receive(CorrectedBinaryMessage message) + { + FleetsyncMessageType messageType = Fleetsync2Message.getMessageType(message); + + switch(messageType) + { + case GPS: + broadcast(new LocationReport(message, System.currentTimeMillis())); + break; + case STATUS: + broadcast(new Status(message, System.currentTimeMillis())); + break; + default: + broadcast(new AutomaticNumberIdentification(message, System.currentTimeMillis())); + break; + } + } + + /** + * Broadcasts the message to an optional registered listener + */ + private void broadcast(IMessage message) + { + if(mMessageListener != null) + { + mMessageListener.receive(message); + } + } + + /** + * Registers the listener to receive fleetsync messages + */ + public void setMessageListener(Listener listener) + { + mMessageListener = listener; + } + + /** + * Unregisters the current listener from receiving fleetsync messages + */ public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncCallEvent.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncCallEvent.java deleted file mode 100644 index 70fb96e7..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncCallEvent.java +++ /dev/null @@ -1,206 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.fleetsync2; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class FleetsyncCallEvent extends CallEvent -{ - private long mFrequency = 0; - - public FleetsyncCallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String details, - long frequency ) - { - super( DecoderType.FLEETSYNC2, callEventType, - aliasList, fromID, toID, details ); - - mFrequency = frequency; - } - - private Alias getAlias( String ident ) - { - if( hasAliasList() ) - { - return getAliasList().getFleetsyncAlias( ident ); - } - - return null; - } - - @Override - public Alias getFromIDAlias() - { - return getAlias( getFromID() ); - } - - @Override - public Alias getToIDAlias() - { - return getAlias( getToID() ); - } - - @Override - public String getChannel() - { - return null; - } - - @Override - public long getFrequency() - { - return mFrequency; - } - - public static class Builder - { - /* Required parameters */ - private CallEventType mCallEventType; - - /* Optional parameters */ - private AliasList mAliasList; - private String mFromID; - private String mToID; - private String mDetails; - private long mFrequency; - - public Builder( CallEventType callEventType ) - { - mCallEventType = callEventType; - } - - public Builder aliasList( AliasList aliasList ) - { - mAliasList = aliasList; - return this; - } - - public Builder frequency( long frequency ) - { - mFrequency = frequency; - return this; - } - - public Builder from( String val ) - { - mFromID = val; - return this; - } - - public Builder details( String details ) - { - mDetails = details; - return this; - } - - public Builder to( String toID ) - { - mToID = toID; - return this; - } - - public FleetsyncCallEvent build() - { - return new FleetsyncCallEvent( this ); - } - } - - /** - * Private constructor for the builder - */ - private FleetsyncCallEvent( Builder builder ) - { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mDetails, - builder.mFrequency ); - } - - public static FleetsyncCallEvent getFleetsync2Event( - FleetsyncMessage message, long frequency ) - { - CallEventType type = CallEventType.UNKNOWN; - StringBuilder sbDetails = new StringBuilder(); - sbDetails.append( "Fleetsync " ); - - switch( message.getMessageType() ) - { - case ACKNOWLEDGE: - type = CallEventType.RESPONSE; - sbDetails.append( "ACK" ); - break; - case ANI: - type = CallEventType.ID_ANI; - break; - case EMERGENCY: - type = CallEventType.EMERGENCY; - break; - case GPS: - type = CallEventType.GPS; - sbDetails.append( message.getGPSLocation() ); - break; - case LONE_WORKER_EMERGENCY: - type = CallEventType.EMERGENCY; - sbDetails.append( "LONE WORKER EMERGENCY ALERT" ); - break; - case PAGING: - type = CallEventType.PAGE; - break; - case STATUS: - type = CallEventType.STATUS; - sbDetails.append( "Status: " ); - sbDetails.append( message.getStatus() ); - - if( message.getStatusAlias() != null ) - { - sbDetails.append( "/" ); - sbDetails.append( message.getStatusAlias().getName() ); - } - break; - default: - break; - } - - if( type == CallEventType.ID_ANI || - type == CallEventType.EMERGENCY ) - { - return new FleetsyncCallEvent.Builder( type ) - .details( sbDetails.toString() ) - .frequency( frequency ) - .from( message.getFromID() ) - .build(); - } - else - { - return new FleetsyncCallEvent.Builder( type ) - .details( sbDetails.toString() ) - .frequency( frequency ) - .from( message.getFromID() ) - .to( message.getToID() ) - .build(); - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessage.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessage.java deleted file mode 100644 index 206aa3fd..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessage.java +++ /dev/null @@ -1,894 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.fleetsync2; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.edac.CRCFleetsync; -import io.github.dsheirer.map.Plottable; -import io.github.dsheirer.message.Message; -import org.jdesktop.swingx.mapviewer.GeoPosition; - -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; - -public class FleetsyncMessage extends Message -{ - private static DecimalFormat mDecimalFormatter = new DecimalFormat("#.#####"); - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); - - //Calendar to use in calculating time hacks - Calendar mCalendar = new GregorianCalendar(); - - //Message parts are identified in big-endian order for correct translation - - //Message Header - private static int[] sRevs = { 4,3,2,1,0 }; - private static int[] sSync = { 20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5 }; - - //Message Block 1 - private static int[] sStatusMessage = { 27,26,25,24,23,22,21 }; - private static int[] sMessageType = { 33,32,31,30,29 }; - - private static int sEmergencyFlag = 22; - private static int sLoneWorkerFlag = 24; - private static int sPagingFlag = 26; - private static int sEndOfTransmissionFlag = 27; - private static int sManualFlag = 28; - private static int sANIFlag = 29; - private static int sStatusFlag = 30; - private static int sAcknowledgeFlag = 31; - //32 - unknown - always 0 - //33 - unknown - always 0 - //34 - unknown - set for ACKNOWLEDGE - private static int sGPSExtensionFlag = 35; - private static int sFleetExtensionFlag = 36; - private static int[] sFleetFrom = { 44,43,42,41,40,39,38,37 }; - private static int[] sIdentFrom = { 56,55,54,53,52,51,50,49,48,47,46,45 }; - private static int[] sIdentTo = { 68,67,66,65,64,63,62,61,60,59,58,57 }; - private static int[] sCRC1 = { 84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69 }; - - //Message Block 2 - private static int[] sFleetTo = { 92,91,90,89,88,87,86,85 }; - private static int[] sCRC2 = { 148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,132 }; - - //Message Block 3 - private static int[] sGPSHours = { 176,175,174,173,172 }; - private static int[] sGPSMinutes = { 182,181,180,179,178,177 }; - private static int[] sGPSSeconds = { 188,187,186,185,184,183 }; - private static int[] sCRC3 = { 212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197 }; - - //Message Block 4 - private static int[] sGPSChecksum = { 220,219,218,217,216,215,214,213 }; - private static int[] sLatitudeDegreesMinutes = { 236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221 }; - private static int[] sLatitudeDecimalMinutes = { 251,250,249,248,247,246,245,244,243,242,241,240,239,238 }; - private static int[] sSpeed = { 276,275,274,273,272,271,270,269,268,267,266,265,264,263,262,261,260,259,258,257,256,255,254,253,252 }; - private static int[] sCRC4 = { 276,275,274,273,272,271,270,269,268,267,266,265,264,263,262,261 }; - - //Message Block 5 - private static int[] sGPSCentury = { 284,283,282,281,280,279,278,277 }; - private static int[] sGPSYear = { 291,290,289,288,287,286,285 }; - private static int[] sGPSMonth = { 295,294,293,292 }; - private static int[] sGPSDay = { 300,299,298,297,296 }; - private static int[] sLongitudeDegreesMinutes = { 316,315,314,313,312,311,310,309,308,307,306,305,304,303,302,301 }; - private static int[] sLongitudeDecimalMinutes = { 331,330,329,328,327,326,325,324,323,322,321,320,319,318 }; - private static int[] sCRC5 = { 340,339,338,337,336,335,334,333,332,331,330,329,328,327,326,325 }; - - //Message Block 6 - private static int[] sGPSUnknown1 = { 352,351,350,349 }; - private static int[] sGPSHeading = { 365,363,362,361,360,359,358,357,356,355,354,353 }; - private static int[] sCRC6 = { 404,403,402,401,400,399,398,397,396,395,394,393,392,391,390,389 }; - - //Message Block 7 - private static int[] sBLK7WhatIsIt = { 444,443,442,441,440,439,438,437,436,435,434,433,432,431,430,429 }; - private static int[] sCRC7 = { 468,467,466,465,464,463,462,461,460,459,458,457,456,455,454,453 }; - - //Message Block 8 - private static int[] sGPSSpeed = { 491,490,489,488,487,486,485,484 }; - private static int[] sGPSSpeedFractional = { 499,498,497,496,495,494,493,492 }; - private static int[] sCRC8 = { 532,531,530,529,528,527,526,525,524,523,522,521,520,519,518,517 }; - - private BinaryMessage mMessage; - private CRC[] mCRC = new CRC[ 8 ]; - private AliasList mAliasList; - - public FleetsyncMessage( BinaryMessage message, AliasList list ) - { - mMessage = message; - mAliasList = list; - checkParity(); - } - - private void checkParity() - { - //Check message block 1 - mCRC[ 0 ] = detectAndCorrect( 21, 85 ); - - //Only check subsequent blocks if we know block 1 is correct - if( mCRC[ 0 ] == CRC.PASSED || mCRC[ 0 ] == CRC.CORRECTED ) - { - if( hasFleetExtensionFlag() ) - { - //Check message block 2 - mCRC[ 1 ] = detectAndCorrect( 85, 149 ); - } - - if( hasGPSFlag() ) - { - //Check message block 3 - mCRC[ 2 ] = detectAndCorrect( 149, 213 ); - //Check message block 4 - mCRC[ 3 ] = detectAndCorrect( 213, 277 ); - //Check message block 5 - mCRC[ 4 ] = detectAndCorrect( 277, 341 ); - //Check message block 6 - mCRC[ 5 ] = detectAndCorrect( 341, 405 ); - //Check message block 7 - mCRC[ 6 ] = detectAndCorrect( 405, 469 ); - //Check message block 8 - mCRC[ 7 ] = detectAndCorrect( 469, 533 ); - } - } - } - - private CRC detectAndCorrect( int start, int end ) - { - BitSet original = mMessage.get( start, end ); - - CRC retVal = CRCFleetsync.check( original ); - - //Attempt to correct single-bit errors - if( retVal == CRC.FAILED_PARITY ) - { - int[] errorBitPositions = CRCFleetsync.findBitErrors( original ); - - if( errorBitPositions != null ) - { - for( int errorBitPosition: errorBitPositions ) - { - mMessage.flip( start + errorBitPosition ); - } - - retVal = CRC.CORRECTED; - } - } - - return retVal; - } - - public boolean isValid() - { - boolean valid = true; - - for( int x = 0; x < mCRC.length; x++ ) - { - CRC crc = mCRC[ x ]; - - if( crc != null ) - { - if( crc == CRC.FAILED_CRC || - crc == CRC.FAILED_PARITY || - crc == CRC.UNKNOWN ) - { - valid = false; - } - } - } - - return valid; - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) ); - - sb.append( " FSync2 " + getParity() ); - - sb.append( getMessage() ); - - sb.append( " [" + mMessage.toString() + "]" ); - - return sb.toString(); - } - - /** - * Pads spaces onto the end of the value to make it 'places' long - */ - public String pad( String value, int places, String padCharacter ) - { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); - } - - /** - * Pads an integer value with additional zeroes to make it decimalPlaces long - */ - public String format( int number, int decimalPlaces ) - { - StringBuilder sb = new StringBuilder(); - - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); - - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - public FleetsyncMessageType getMessageType() - { - if( hasStatusFlag() ) - { - if( hasAcknowledgeFlag() ) - { - return FleetsyncMessageType.ACKNOWLEDGE; - } - - if( hasGPSFlag() ) - { - return FleetsyncMessageType.GPS; - } - else - { - return FleetsyncMessageType.STATUS; - } - } - else - { - if( hasAcknowledgeFlag() ) - { - return FleetsyncMessageType.ACKNOWLEDGE; - } - - if( hasANIFlag() ) - { - return FleetsyncMessageType.ANI; - } - - if( hasGPSFlag() ) - { - return FleetsyncMessageType.GPS; - } - - if( hasPagingFlag() ) - { - return FleetsyncMessageType.PAGING; - } - - if( hasEmergencyFlag() ) - { - if( hasLoneWorkerFlag() ) - { - return FleetsyncMessageType.LONE_WORKER_EMERGENCY; - } - else - return FleetsyncMessageType.EMERGENCY; - } - } - - return FleetsyncMessageType.UNKNOWN; - } - - public int getStatus() - { - return getStatusNumber() + 9; - } - - /** - * Returns the RAW status number. The actual status number should be - * accessed via the getStatus() method. - * @return - */ - public int getStatusNumber() - { - return getInt( sStatusMessage ); - } - - /** - * @return - */ - public Alias getStatusAlias() - { - if( mAliasList != null ) - { - return mAliasList.getStatus( getStatus() ); - } - - return null; - } - - public int getFleetFrom() - { - return getInt( sFleetFrom ) + 99; - } - - public int getIdentifierFrom() - { - return getInt( sIdentFrom ) + 999; - } - - /** - * Inverted Flags - 0 = flag is true - * @return - */ - public boolean hasEndOfTransmissionFlag() - { - return !mMessage.get( sEndOfTransmissionFlag ); - } - - public boolean hasEmergencyFlag() - { - return !mMessage.get( sEmergencyFlag ); - } - - public boolean hasLoneWorkerFlag() - { - return !mMessage.get( sLoneWorkerFlag ); - } - - public boolean hasPagingFlag() - { - return !mMessage.get( sPagingFlag ); - } - - - /** - * Normal Flags - 1 = flag is true - * @return - */ - public boolean hasANIFlag() - { - return mMessage.get( sANIFlag ); - } - - public boolean hasAcknowledgeFlag() - { - return mMessage.get( sAcknowledgeFlag ); - } - - public boolean hasFleetExtensionFlag() - { - return mMessage.get( sFleetExtensionFlag ); - } - - public boolean hasGPSFlag() - { - return mMessage.get( sGPSExtensionFlag ); - } - - public boolean hasStatusFlag() - { - return mMessage.get( sStatusFlag ); - } - - - public int getFleetTo() - { - if( hasFleetExtensionFlag() ) - { - return getInt( sFleetTo ) + 99; - } - else - { - return getInt( sFleetFrom ) + 99; - } - } - - public int getIdentifierTo() - { - return getInt( sIdentTo ) + 999; - } - - /** - * GPS Heading - * @return - */ - public double getHeading() - { - double retVal = 0.0; - - int heading = getInt( sGPSHeading ); - - if( heading != 4095 ) - { - retVal = (double)(heading / 10.0 ); - } - - return retVal; - } - - public int getBlock7WhatIsIt() - { - int value = getInt( sBLK7WhatIsIt ); - - if( value == 65535 ) - { - value = -1; - } - - return value; - } - - /** - * Speed in Kph - whole numbers and three decimal digits of precision - * @return - */ - public double getSpeed() - { - double retVal = 0.0; - - int temp = getInt( sSpeed ); - - if( temp != 0 ) - { - retVal = (double)temp / 1000.0D; - } - return retVal; - } - - public double getLatitude() - { - //TODO: determine the correct hemisphere indicator and replace this - //hardcoded "0" with the correct hemisphere value - - return convertDDMToDD( 0, - getInt( sLatitudeDegreesMinutes ), - getInt( sLatitudeDecimalMinutes ) ); - } - - public double getLongitude() - { - //TODO: determine the correct hemisphere indicator and replace this - //hardcoded "1" with the correct hemisphere value - - return convertDDMToDD( 1, - getInt( sLongitudeDegreesMinutes ), - getInt( sLongitudeDecimalMinutes ) ); - } - - /** - * Converts Degrees Decimal Minutes to Decimal Degrees - * - * Latitude and Longitude values are represented by: - * - * @param hemisphere - 0=North & East, 1=South & West - * @param degreesMinutes - an integer value with the first 2-3 digits representing - * the degrees and the last two digits representing the minutes - * - * @param decimalDegrees - an integer value representing the fractional - * minutes - * - * @return - decimal degrees formatted value - */ - public double convertDDMToDD( int hemisphere, int degreesMinutes, int decimalDegrees ) - { - double retVal = 0.0; - - if( degreesMinutes != 0 ) - { - //Degrees - divide value by 100 and retain the whole number value (ie degrees) - retVal += (double)( degreesMinutes / 100 ); - - //Minutes - modulus by 100 to get the whole minutes value - int wholeMinutes = degreesMinutes % 100; - - if( wholeMinutes != 0 ) - { - retVal += (double)( wholeMinutes / 60.0D ); - } - } - - if( decimalDegrees != 0 ) - { - //Fractional Minutes - divide by 10,000 to get the decimal place correct - //then divide by 60 (minutes) to get the decimal value - //10,000 * 60 = 600,000 - retVal += (double)( decimalDegrees / 600000.0D ); - } - - //Adjust the value +/- for the hemisphere - if( hemisphere == 1 ) //South and West values - { - retVal = -retVal; - } - - return retVal; - } - - public String getGPSTime() - { - StringBuilder sb = new StringBuilder(); - sb.append( format( getInt( sGPSHours ), 2 ) ); - sb.append( ":" ); - sb.append( format( getInt( sGPSMinutes ), 2 ) ); - sb.append( ":" ); - sb.append( format( getInt( sGPSSeconds ), 2 ) ); - sb.append( "z" ); - - return sb.toString(); - } - - - /** - * 7 bit checksum that is part of the GPGGA message, I think - * @return - */ - public int getGPSChecksum() - { - return getInt( sGPSChecksum ); - } - - public String getGPSChecksumString() - { - String sum = Integer.toHexString( getGPSChecksum() ); - - if( sum.length() == 1 ) - { - return "*0" + sum; - } - else - { - return "*" + sum; - } - } - - /** - * Returns 1-based calendar day of month - * 1 = 1st day of month - */ - public int getGPSDay() - { - return getInt( sGPSDay ) + 1; - } - - /** - * Returns 0-based GPS Month - * 0 = January - * - * Note: actual day of month value is 1-based, so we subtract 1 to get - * the actual day of month value - */ - public int getGPSMonth() - { - return getInt( sGPSMonth ); - } - - /** - * Returns year, which is a combination of the century bit field + 1, times - * 100, plus the year bit field - */ - public int getGPSYear() - { -// return ( ( getInt( sGPSCentury ) + 1 ) * 100 ) + getInt( sGPSYear ); - return ( 2000 ) + getInt( sGPSYear ); - } - - public String getGPSLocation() - { - StringBuilder sb = new StringBuilder(); - sb.append( pad( String.valueOf( mDecimalFormatter.format( getLatitude() ) ), 8, "0" ) + " " ); - sb.append( pad( String.valueOf( mDecimalFormatter.format( getLongitude() ) ), 10, "0" ) ); - - return sb.toString(); - } - - - public String getGPSDate() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getGPSYear() + "-" ); - - int month = getGPSMonth(); - - if( month < 10 ) - { - sb.append( "0" ); - } - - sb.append( month + "-" ); - - int day = getGPSDay(); - - if( day < 10 ) - { - sb.append( "0" ); - } - - sb.append( day ); - - return sb.toString(); - } - - private int getInt( int[] bits ) - { - int retVal = 0; - - for( int x = 0; x < bits.length; x++ ) - { - if( mMessage.get( bits[ x ] ) ) - { - retVal += 1< getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - Alias status = getStatusAlias(); - - if( status != null ) - { - aliases.add( status ); - } - - return aliases; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java index fdd2f2a6..c40039bd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java @@ -1,56 +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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.fleetsync2; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -public class FleetsyncMessageFilter extends Filter +public class FleetsyncMessageFilter extends Filter { - private HashMap> mElements = - new HashMap>(); - - public FleetsyncMessageFilter() - { - super( "Fleetsync Message Filter" ); + private HashMap> mElements = + new HashMap>(); - for( FleetsyncMessageType type: FleetsyncMessageType.values() ) - { - if( type != FleetsyncMessageType.UNKNOWN ) - { - mElements.put( type, new FilterElement( type ) ); - } - } - } - - @Override - public boolean passes( Message message ) + public FleetsyncMessageFilter() { - if( mEnabled && canProcess( message ) ) - { - FleetsyncMessage fleet = (FleetsyncMessage)message; - - if( mElements.containsKey( fleet.getMessageType() ) ) - { - return mElements.get( fleet.getMessageType() ).isEnabled(); - } - } - - return false; + super("Fleetsync Message Filter"); + + for(FleetsyncMessageType type : FleetsyncMessageType.values()) + { + if(type != FleetsyncMessageType.UNKNOWN) + { + mElements.put(type, new FilterElement(type)); + } + } } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof FleetsyncMessage; + if(mEnabled && canProcess(message)) + { + Fleetsync2Message fleet = (Fleetsync2Message)message; + + if(mElements.containsKey(fleet.getMessageType())) + { + return mElements.get(fleet.getMessageType()).isEnabled(); + } + } + + return false; } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof Fleetsync2Message; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mElements.values() ); + return new ArrayList>(mElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncIdentifier.java new file mode 100644 index 00000000..49dedc14 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncIdentifier.java @@ -0,0 +1,88 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.fleetsync2.identifier; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Fleetsync talkgroup identifier + */ +public class FleetsyncIdentifier extends TalkgroupIdentifier implements Comparable +{ + public FleetsyncIdentifier(Integer talkgroup, Role role) + { + super(talkgroup, role, false); + } + + @Override + public Protocol getProtocol() + { + return Protocol.FLEETSYNC; + } + + /** + * This identifier formatted as FFF:IIII where F=fleet and I=ident + */ + public String formatted() + { + return String.format("%03d:%04d", getFleet(), getIdent()); + } + + /** + * Fleet prefix for the identifier + */ + public int getFleet() + { + return (getValue() >> 12) & 0xFF; + } + + /** + * Unit identifier + */ + public int getIdent() + { + return getValue() & 0xFFF; + } + + /** + * Creates a fleetsync identifier from the integer value that contains both the fleet and the ident with a FROM role + */ + public static FleetsyncIdentifier createFromUser(int talkgroup) + { + return new FleetsyncIdentifier(talkgroup, Role.FROM); + } + + /** + * Creates a fleetsync identifier from the integer value that contains both the fleet and the ident with a TO role + */ + public static FleetsyncIdentifier createToUser(int talkgroup) + { + return new FleetsyncIdentifier(talkgroup, Role.TO); + } + + @Override + public int compareTo(FleetsyncIdentifier o) + { + return getValue().compareTo(o.getValue()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncStatus.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncStatus.java new file mode 100644 index 00000000..775d9351 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/identifier/FleetsyncStatus.java @@ -0,0 +1,59 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.fleetsync2.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Fleetsync User status + */ +public class FleetsyncStatus extends IntegerIdentifier +{ + private Role mRole; + + /** + * Constructs an APCO-25 status + * @param status value + * @param role of the user/unit that the status applies to + */ + public FleetsyncStatus(int status, IdentifierClass identifierClass, Form form, Role role) + { + super(status, identifierClass, form, role); + mRole = role; + } + + @Override + public Protocol getProtocol() + { + return Protocol.FLEETSYNC; + } + + /** + * Creates a User status + */ + public static FleetsyncStatus createUserStatus(int status) + { + return new FleetsyncStatus(status, IdentifierClass.USER, Form.USER_STATUS, Role.FROM); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/AutomaticNumberIdentification.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/AutomaticNumberIdentification.java new file mode 100644 index 00000000..6d349a34 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/AutomaticNumberIdentification.java @@ -0,0 +1,93 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.fleetsync2.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * Automatic Number Identification (ANI). + */ +public class AutomaticNumberIdentification extends Fleetsync2Message +{ + private List mIdentifers; + + public AutomaticNumberIdentification(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + protected int getBlockCount() + { + return hasFleetExtensionFlag(getMessage()) ? 2 : 1; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("FROM:").append(getFromIdentifier().formatted()); + + if(hasEmergencyFlag(getMessage())) + { + sb.append(" EMERGENCY"); + } + + if(hasLoneWorkerFlag(getMessage())) + { + sb.append(" LONE WORKER"); + } + + if(hasPagingFlag(getMessage())) + { + sb.append(" PAGING"); + } + + if(hasAcknowledgeFlag(getMessage())) + { + sb.append(" ACKNOWLEDGE"); + } + + if(hasANIFlag(getMessage())) + { + sb.append(" ANI"); + } + + return sb.toString().trim(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifers == null) + { + mIdentifers = new ArrayList<>(); + mIdentifers.add(getFromIdentifier()); + } + + return mIdentifers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Fleetsync2Message.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Fleetsync2Message.java new file mode 100644 index 00000000..2aebf9d3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Fleetsync2Message.java @@ -0,0 +1,304 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.fleetsync2.message; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.edac.CRCFleetsync; +import io.github.dsheirer.message.Message; +import io.github.dsheirer.module.decode.fleetsync2.FleetsyncMessageType; +import io.github.dsheirer.module.decode.fleetsync2.identifier.FleetsyncIdentifier; +import io.github.dsheirer.protocol.Protocol; + +import java.util.BitSet; + +public abstract class Fleetsync2Message extends Message +{ + //Message Header + private static int[] BIT_REVERSALS = {0, 1, 2, 3, 4}; + private static int[] SYNC_PATTERN = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; + + //Message Block 1 + private static int[] STATUS = {21, 22, 23, 24, 25, 26, 27}; + private static int[] MESSAGE_TYPE = {29, 30, 31, 32, 33}; + + //Message Type Flags + private static int FLAG_UNKNOWN_1 = 21; + private static int FLAG_EMERGENCY_WORKER = 22; + private static int FLAG_UNKNOWN_2 = 23; + private static int FLAG_LONE_WORKER = 24; + private static int FLAG_UNKNOWN_3 = 25; + private static int FLAG_PAGING = 26; + private static int FLAG_END_OF_TRANSMISSION = 27; + private static int FLAG_MANUAL = 28; + private static int FLAG_AUTOMATIC_NUMBER_IDENTIFIER = 29; + private static int FLAG_STATUS = 30; + private static int FLAG_ACKNOWLEDGE = 31; + private static int FLAG_UNKNOWN_4 = 32; + private static int FLAG_UNKNOWN_5 = 33; + private static int FLAG_UNKNOWN_6 = 34; //Always set for Acknowledge + private static int FLAG_GPS_EXTENSION = 35; + private static int FLAG_FLEET_EXTENSION = 36; + + //From and To talkgroups share a common fleet value for single-block messages. + private static int[] FLEET = {37, 38, 39, 40, 41, 42, 43, 44}; + private static int[] FROM_IDENT = {45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56}; + private static int[] TO_IDENT = {57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68}; + + private static int[] CRC_BLOCK_1 = {69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84}; + + //Message Block 2 + private static int[] FLEET_TO = {85, 86, 87, 88, 89, 90, 91, 92}; + private static int[] CRC_BLOCK_2 = {132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148}; + + private CorrectedBinaryMessage mMessage; + private CRC[] mCRC = new CRC[2]; + private FleetsyncIdentifier mFromIdentifier; + private FleetsyncIdentifier mToIdentifier; + + public Fleetsync2Message(CorrectedBinaryMessage message, long timestamp) + { + super(timestamp); + mMessage = message; + checkParity(); + } + + protected CorrectedBinaryMessage getMessage() + { + return mMessage; + } + + @Override + public Protocol getProtocol() + { + return Protocol.FLEETSYNC; + } + + /** + * Indicates the number of 64-bit blocks that makeup this message. + */ + protected abstract int getBlockCount(); + + /** + * Fleetsync identifier for the from or transmitting radio. + */ + public FleetsyncIdentifier getFromIdentifier() + { + if(mFromIdentifier == null) + { + mFromIdentifier = FleetsyncIdentifier.createFromUser(getCommonFleet() + getFromIdent()); + } + + return mFromIdentifier; + } + + /** + * Fleetsync identifier for the to or receiving radio + */ + public FleetsyncIdentifier getToIdentifier() + { + if(mToIdentifier == null) + { + int value = (hasFleetExtensionFlag(getMessage()) ? getExtendedFleet() : getCommonFleet()) + getToIdent(); + mToIdentifier = FleetsyncIdentifier.createToUser(value); + } + + return mToIdentifier; + } + + /** + * Shared or common fleet value. Note: this value is added to 99 and then left shifted by 12 to obtain fleets in + * the range of 1 - 127. + */ + private int getCommonFleet() + { + return (getMessage().getInt(FLEET) + 99) << 12; + } + + /** + * Extended fleet for the TO identifier. This value is only valid when the extended fleet flag is set. + * Note: this value is added to 99 and then left shifted by 12 to obtain fleets in the range of 1 - 127. + */ + private int getExtendedFleet() + { + return (getMessage().getInt(FLEET_TO) + 99) << 12; + } + + /** + * From ident value. Transmitted value is added to 999 to obtain ident values in range of 1000 - 4996 + */ + private int getFromIdent() + { + return getMessage().getInt(FROM_IDENT) + 999; + } + + /** + * To ident value. Transmitted value is added to 999 to obtain ident values in range of 1000 - 4996 + */ + private int getToIdent() + { + return getMessage().getInt(TO_IDENT) + 999; + } + + private void checkParity() + { + //Check message block 1 + CRC block1Crc = detectAndCorrect(getMessage(), 21, 85); + + mCRC = new CRC[getBlockCount()]; + mCRC[0] = block1Crc; + + for(int x = 1; x < getBlockCount(); x++) + { + int blockStart = 21 + (x * 64); + mCRC[x] = detectAndCorrect(getMessage(), blockStart, blockStart + 64); + } + } + + public static CRC detectAndCorrect(CorrectedBinaryMessage message, int start, int end) + { + BitSet original = message.get(start, end); + + CRC retVal = CRCFleetsync.check(original); + + //Attempt to correct single-bit errors + if(retVal == CRC.FAILED_PARITY) + { + int[] errorBitPositions = CRCFleetsync.findBitErrors(original); + + if(errorBitPositions != null) + { + for(int errorBitPosition : errorBitPositions) + { + message.flip(start + errorBitPosition); + message.incrementCorrectedBitCount(1); + } + + retVal = CRC.CORRECTED; + } + } + + return retVal; + } + + public boolean isValid() + { + return mCRC[0].passes(); + } + + /** + * Type of message + */ + public FleetsyncMessageType getMessageType() + { + return getMessageType(getMessage()); + } + + public static FleetsyncMessageType getMessageType(BinaryMessage message) + { + if(hasAcknowledgeFlag(message)) + { + return FleetsyncMessageType.ACKNOWLEDGE; + } + + if(hasGPSFlag(message)) + { + return FleetsyncMessageType.GPS; + } + + if(hasStatusFlag(message)) + { + return FleetsyncMessageType.STATUS; + } + + if(hasANIFlag(message)) + { + return FleetsyncMessageType.ANI; + } + + if(hasPagingFlag(message)) + { + return FleetsyncMessageType.PAGING; + } + + if(hasLoneWorkerFlag(message) && hasEmergencyFlag(message)) + { + return FleetsyncMessageType.LONE_WORKER_EMERGENCY; + } + + return FleetsyncMessageType.UNKNOWN; + } + + /** + * Message Type Indicator Flags. The flags have inverted values where 0 = flag is true + */ + public static boolean hasEndOfTransmissionFlag(BinaryMessage message) + { + return !message.get(FLAG_END_OF_TRANSMISSION); + } + + public static boolean hasEmergencyFlag(BinaryMessage message) + { + return !message.get(FLAG_EMERGENCY_WORKER); + } + + public static boolean hasLoneWorkerFlag(BinaryMessage message) + { + return !message.get(FLAG_LONE_WORKER); + } + + public static boolean hasPagingFlag(BinaryMessage message) + { + return !message.get(FLAG_PAGING); + } + + + /** + * Message Type Indicator Flags. The flags have normal values where 1 = true + * + * @return + */ + public static boolean hasANIFlag(BinaryMessage message) + { + return message.get(FLAG_AUTOMATIC_NUMBER_IDENTIFIER); + } + + public static boolean hasAcknowledgeFlag(BinaryMessage message) + { + return message.get(FLAG_ACKNOWLEDGE); + } + + public static boolean hasFleetExtensionFlag(BinaryMessage message) + { + return message.get(FLAG_FLEET_EXTENSION); + } + + public static boolean hasGPSFlag(BinaryMessage message) + { + return message.get(FLAG_GPS_EXTENSION); + } + + public static boolean hasStatusFlag(BinaryMessage message) + { + return message.get(FLAG_STATUS); + } + +} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/LocationReport.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/LocationReport.java new file mode 100644 index 00000000..f7499dc5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/LocationReport.java @@ -0,0 +1,250 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.fleetsync2.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import org.jdesktop.swingx.mapviewer.GeoPosition; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; + +/** + * Status message containing a numeric status indicator where the numeric value is pre-defined. + */ +public class LocationReport extends Fleetsync2Message +{ + //Message Block 3 + private static int[] GPS_HOURS = {172, 173, 174, 175, 176}; + private static int[] GPS_MINUTES = {177, 178, 179, 180, 181, 182}; + private static int[] GPS_SECONDS = {183, 184, 185, 186, 187, 188}; + + //Message Block 4 + private static int[] GPS_CHECKSUM = {213, 214, 215, 216, 217, 218, 219, 220}; + private static int[] LATITUDE_DEGREES_MINUTES = {221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236}; + private static int[] LATITUDE_FRACTIONAL_MINUTES = {238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251}; + private static int[] SPEED = {252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276}; + + //Message Block 5 + private static int[] GPS_CENTURY = {277, 278, 279, 280, 281, 282, 283, 284}; + private static int[] GPS_YEAR = {284, 286, 287, 288, 289, 290, 291}; + private static int[] GPS_MONTH = {292, 293, 294, 295}; + private static int[] GPS_DAY = {296, 297, 298, 299, 300}; + private static int[] LONGITUDE_DEGREES_MINUTES = {301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316}; + + //TODO: this overlaps with the CRC block address space ... + private static int[] LONGITUDE_FRACTIONAL_MINUTES = {318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331}; + + //Message Block 6 + private static int[] GPS_HEADING = {353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365}; + + //Message Block 7 + + //Message Block 8 + private static int[] GPS_SPEED = {484, 485, 486, 487, 488, 489, 490, 491}; + private static int[] GPS_SPEED_FRACTIONAL = {492, 493, 494, 495, 496, 497, 498, 499}; + + private GeoPosition mGeoPosition; + private Long mGPSTimestamp; + private Double mGPSHeading; + private Double mGPSSpeed; + private List mIdentifers; + + public LocationReport(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + protected int getBlockCount() + { + return 8; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("GPS FROM:").append(getFromIdentifier()); + sb.append(" TO:").append(getToIdentifier()); + sb.append(" ").append(getLocation()); + sb.append(" HEADING:").append(getHeading()); + sb.append(" SPEED:").append(getSpeed()); + sb.append(" TIME:").append(new Date(getGPSTime())); + + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifers == null) + { + mIdentifers = new ArrayList<>(); + mIdentifers.add(getFromIdentifier()); + mIdentifers.add(getToIdentifier()); + } + + return mIdentifers; + } + + /** + * GPS Heading in degrees relative to true North. + */ + public double getHeading() + { + if(mGPSHeading == null) + { + if(hasHeading()) + { + mGPSHeading = (double)getHeadingValue() / 10.0; + } + else + { + mGPSHeading = 0.0; + } + } + + return mGPSHeading; + } + + private int getHeadingValue() + { + return getMessage().getInt(GPS_HEADING); + } + + private boolean hasHeading() + { + return getHeadingValue() != 4095; + } + + /** + * Speed in kph + */ + public double getSpeed() + { + if(mGPSSpeed == null) + { + mGPSSpeed = (double)getMessage().getInt(GPS_SPEED) + ((double)getMessage().getInt(GPS_SPEED_FRACTIONAL) / 255.0); + } + + return mGPSSpeed; + } + + /** + * Latitude and Longitude location/position. + */ + public GeoPosition getLocation() + { + if(mGeoPosition == null) + { + mGeoPosition = new GeoPosition(getLatitude(), getLongitude()); + } + + return mGeoPosition; + } + + private double getLatitude() + { + //TODO: determine the correct hemisphere indicator and replace this + return convertDDMToDD(0, getMessage().getInt(LATITUDE_DEGREES_MINUTES), + getMessage().getInt(LATITUDE_FRACTIONAL_MINUTES)); + } + + private double getLongitude() + { + //TODO: determine the correct hemisphere indicator and replace this + return convertDDMToDD(1, getMessage().getInt(LONGITUDE_DEGREES_MINUTES), + getMessage().getInt(LONGITUDE_FRACTIONAL_MINUTES)); + } + + /** + * Converts Degrees Decimal Minutes to Decimal Degrees + * + * Latitude and Longitude values are represented by: + * + * @param hemisphere - 0=North & East, 1=South & West + * @param degreesMinutes - an integer value with the first 2-3 digits representing + * the degrees and the last two digits representing the minutes + * @param decimalDegrees - an integer value representing the fractional + * minutes + * @return - decimal degrees formatted value + */ + public double convertDDMToDD(int hemisphere, int degreesMinutes, int decimalDegrees) + { + double retVal = 0.0; + + if(degreesMinutes != 0) + { + //Degrees - divide value by 100 and retain the whole number value (ie degrees) + retVal += (double)(degreesMinutes / 100); + + //Minutes - modulus by 100 to get the whole minutes value + int wholeMinutes = degreesMinutes % 100; + + if(wholeMinutes != 0) + { + retVal += (double)(wholeMinutes / 60.0D); + } + } + + if(decimalDegrees != 0) + { + //Fractional Minutes - divide by 10,000 to get the decimal place correct + //then divide by 60 (minutes) to get the decimal value + //10,000 * 60 = 600,000 + retVal += (double)(decimalDegrees / 600000.0D); + } + + //Adjust the value +/- for the hemisphere + if(hemisphere == 1) //South and West values + { + retVal = -retVal; + } + + return retVal; + } + + /** + * GPS Timestamp in milliseconds + */ + public long getGPSTime() + { + if(mGPSTimestamp == null) + { + Calendar calendar = new GregorianCalendar(); + calendar.add(Calendar.YEAR, 2000 + getMessage().getInt(GPS_YEAR)); + calendar.add(Calendar.MONTH, getMessage().getInt(GPS_MONTH)); + calendar.add(Calendar.DAY_OF_MONTH, getMessage().getInt(GPS_DAY)); + calendar.add(Calendar.HOUR_OF_DAY, getMessage().getInt(GPS_HOURS)); + calendar.add(Calendar.MINUTE, getMessage().getInt(GPS_MINUTES)); + calendar.add(Calendar.SECOND, getMessage().getInt(GPS_SECONDS)); + + mGPSTimestamp = calendar.getTimeInMillis(); + } + + return mGPSTimestamp; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Status.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Status.java new file mode 100644 index 00000000..09e7eae3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/message/Status.java @@ -0,0 +1,110 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.fleetsync2.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.fleetsync2.identifier.FleetsyncStatus; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status message containing a numeric status indicator where the numeric value is pre-defined. + */ +public class Status extends Fleetsync2Message +{ + private static int[] STATUS = {21, 22, 23, 24, 25, 26, 27}; + + private FleetsyncStatus mStatusIdentifier; + private List mIdentifers; + + public Status(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + protected int getBlockCount() + { + return hasFleetExtensionFlag(getMessage()) ? 2 : 1; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("FROM:").append(getFromIdentifier()).append(" TO:").append(getToIdentifier()); + + sb.append(" STATUS:").append(getStatus()); + + if(hasEmergencyFlag(getMessage())) + { + sb.append(" EMERGENCY"); + } + + if(hasLoneWorkerFlag(getMessage())) + { + sb.append(" LONE WORKER"); + } + + if(hasPagingFlag(getMessage())) + { + sb.append(" PAGING"); + } + + if(hasAcknowledgeFlag(getMessage())) + { + sb.append(" ACKNOWLEDGE"); + } + + if(hasANIFlag(getMessage())) + { + sb.append(" ANI"); + } + + return sb.toString(); + } + + public FleetsyncStatus getStatus() + { + if(mStatusIdentifier == null) + { + mStatusIdentifier = FleetsyncStatus.createUserStatus(getMessage().getInt(STATUS)); + } + + return mStatusIdentifier; + } + + @Override + public List getIdentifiers() + { + if(mIdentifers == null) + { + mIdentifers = new ArrayList<>(); + mIdentifers.add(getFromIdentifier()); + mIdentifers.add(getToIdentifier()); + mIdentifers.add(getStatus()); + } + + return mIdentifers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/Header.java b/src/main/java/io/github/dsheirer/module/decode/ip/Header.java new file mode 100644 index 00000000..00ce9941 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/Header.java @@ -0,0 +1,74 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; + +import io.github.dsheirer.bits.BinaryMessage; + +public abstract class Header implements IHeader +{ + private BinaryMessage mMessage; + private int mOffset; + private boolean mValid = true; + + /** + * 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 Header(BinaryMessage message, int offset) + { + mMessage = message; + mOffset = offset; + } + + /** + * Underlying message that contains this header + */ + public BinaryMessage getMessage() + { + return mMessage; + } + + /** + * Offset to this header in the underlying message + */ + public int getOffset() + { + return mOffset; + } + + /** + * Indicates if this header is valid + */ + @Override + public boolean isValid() + { + return mValid; + } + + /** + * Sets the valid flag for this header + */ + protected void setValid(boolean valid) + { + mValid = valid; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/IHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/IHeader.java new file mode 100644 index 00000000..8def15a1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/IHeader.java @@ -0,0 +1,39 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; + +/** + * Data Packet Header + */ +public interface IHeader +{ + /** + * Indicates that this header is valid and that it meets the length requirements. + */ + boolean isValid(); + + /** + * Indicates the length of this header. An implicit understanding is that any data that follows + * the header (ie payload) starts after the header length. + * @return length of this header in bytes. + */ + int getLength(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/IPProtocol.java b/src/main/java/io/github/dsheirer/module/decode/ip/IPProtocol.java similarity index 80% rename from src/main/java/io/github/dsheirer/module/decode/p25/reference/IPProtocol.java rename to src/main/java/io/github/dsheirer/module/decode/ip/IPProtocol.java index 865d3f0b..bcb231b6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/IPProtocol.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/IPProtocol.java @@ -1,4 +1,24 @@ -package io.github.dsheirer.module.decode.p25.reference; +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; public enum IPProtocol { diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/IPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/IPacket.java new file mode 100644 index 00000000..90d1e4f6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/IPacket.java @@ -0,0 +1,43 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; + +/** + * Data packet interface + */ +public interface IPacket +{ + /** + * Header for the data packet. + */ + public IHeader getHeader(); + + /** + * Optional payload for the packet + * @return null if there is no payload + */ + public IPacket getPayload(); + + /** + * Indicates if this packet contains an optional payload + */ + public boolean hasPayload(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/Packet.java b/src/main/java/io/github/dsheirer/module/decode/ip/Packet.java new file mode 100644 index 00000000..8392fcb0 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/Packet.java @@ -0,0 +1,62 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; + +import io.github.dsheirer.bits.BinaryMessage; + +public abstract class Packet implements IPacket +{ + private BinaryMessage mMessage; + private int mOffset; + + /** + * Constructs a parser for a header contained within a binary message starting at the offset. + * @param message containing the packet + * @param offset to the packet within the message + */ + public Packet(BinaryMessage message, int offset) + { + mMessage = message; + mOffset = offset; + } + + /** + * Underlying message that contains this packet + */ + public BinaryMessage getMessage() + { + return mMessage; + } + + /** + * Offset to this packet in the underlying message + */ + public int getOffset() + { + return mOffset; + } + + @Override + public boolean hasPayload() + { + return getPayload() != null; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/PacketMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/ip/PacketMessageFactory.java new file mode 100644 index 00000000..fc4ee771 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/PacketMessageFactory.java @@ -0,0 +1,108 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +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.ipv4.IPV4Header; +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.message.pdu.packet.sndcp.SNDCPPacketHeader; +import io.github.dsheirer.module.decode.p25.reference.IPHeaderCompression; + +/** + * Message parser factory for packet based binary sequences. + */ +public class PacketMessageFactory +{ + /** + * Creates an IP packet message parser + * + * @param message containing an IP packet + * @param offset to the IP packet within the message + * @return constructed packet message parser + */ + public static IPacket create(SNDCPPacketHeader sndcpPacketHeader, BinaryMessage message, int offset) + { + if(sndcpPacketHeader.getIPHeaderCompression() == IPHeaderCompression.NONE) + { + int version = IPV4Header.getIPVersion(message, offset); + + switch(version) + { + case 4: + return new IPV4Packet(message, offset); + default: + return new UnknownPacket(message, offset); + } + } + else + { + return new UnknownCompressedHeaderPacket(sndcpPacketHeader, message, offset); + } + } + + /** + * Creates an IP packet protocol payload message parser. + * + * @param protocol of the IP packet payload + * @param binaryMessage containing the IP packet and payload + * @param offset to the start of the IP packet payload + * @return constructed packet message parser + */ + public static IPacket create(IPProtocol protocol, BinaryMessage binaryMessage, int offset) + { + switch(protocol) + { + case UDP: + return new UDPPacket(binaryMessage, offset); + default: + return new UnknownPacket(binaryMessage, offset); + } + } + + /** + * Creates a UDP/IP packet payload parser + * + * @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) + { + switch(destinationPort) + { + case 4001: //Location Service + break; + case 4004: //XCMP Service + break; + case 4005: //Automatic Registration Service + return new ARSPacket(binaryMessage, offset); + case 4007: //Text Message Service + break; + case 4008: //Telemetry Service + break; + } + + return new UnknownPacket(binaryMessage, offset); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/UnknownCompressedHeaderPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/UnknownCompressedHeaderPacket.java new file mode 100644 index 00000000..e8fe2b59 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/UnknownCompressedHeaderPacket.java @@ -0,0 +1,90 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacketHeader; + +public class UnknownCompressedHeaderPacket implements IPacket +{ + private SNDCPPacketHeader mSNDCPPacketHeader; + private BinaryMessage mMessage; + private int mOffset; + + public UnknownCompressedHeaderPacket(SNDCPPacketHeader SNDCPPacketHeader, BinaryMessage message, int offset) + { + mSNDCPPacketHeader = SNDCPPacketHeader; + mMessage = message; + mOffset = offset; + } + + public SNDCPPacketHeader getSNDCPHeader() + { + return mSNDCPPacketHeader; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getSNDCPHeader()); + sb.append(" PACKET:"); + + if(getMessage().size() > getOffset()) + { + sb.append(mMessage.getSubMessage(getOffset(), getMessage().size()).toHexString()); + } + else + { + sb.append("(EMPTY)"); + } + + return sb.toString(); + } + + public BinaryMessage getMessage() + { + return mMessage; + } + + public int getOffset() + { + return mOffset; + } + + @Override + public IHeader getHeader() + { + return null; + } + + @Override + public IPacket getPayload() + { + return null; + } + + @Override + public boolean hasPayload() + { + return false; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/UnknownPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/UnknownPacket.java new file mode 100644 index 00000000..e154583e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/UnknownPacket.java @@ -0,0 +1,81 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip; + +import io.github.dsheirer.bits.BinaryMessage; + +public class UnknownPacket implements IPacket +{ + private BinaryMessage mMessage; + private int mOffset; + + public UnknownPacket(BinaryMessage message, int offset) + { + mMessage = message; + mOffset = offset; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("PACKET:"); + + if(getMessage().size() > getOffset()) + { + sb.append(mMessage.getSubMessage(getOffset(), getMessage().size()).toHexString()); + } + else + { + sb.append("(EMPTY)"); + } + + return sb.toString(); + } + + public BinaryMessage getMessage() + { + return mMessage; + } + + public int getOffset() + { + return mOffset; + } + + @Override + public IHeader getHeader() + { + return null; + } + + @Override + public IPacket getPayload() + { + return null; + } + + @Override + public boolean hasPayload() + { + return false; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java new file mode 100644 index 00000000..6f767e9a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java @@ -0,0 +1,144 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.ip.Header; + +public class ARSHeader extends Header +{ + private static final int[] LENGTH = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int HEADER_EXTENSION_FLAG = 16; + private static final int ACKNOWLEDGEMENT_FLAG = 17; + private static final int PRIORITY_FLAG = 18; + private static final int CONTROL_USER_FLAG = 19; + private static final int[] PDU_TYPE = {20, 21, 22, 23}; + + /** + * 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 ARSHeader(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ARS ").append(getPDUType()); + + if(getPDUType() == ARSPDUType.UNKNOWN) + { + sb.append(" PACKET:").append(getMessage().toHexString()); + } + + return sb.toString(); + } + + /** + * Checks that the underlying message is long enough to contain the PDU + */ + private void checkValid() + { + setValid(getMessage().size() >= (getOffset() + getLength() + getPayloadLength())); + } + + /** + * Indicates the type of ARS PDU + */ + public ARSPDUType getPDUType() + { + return getPDUType(getMessage(), getOffset()); + } + + /** + * Indicates the type of ARS PDU contained in the message starting at the offset index. + */ + public static ARSPDUType getPDUType(BinaryMessage message, int offset) + { + return ARSPDUType.fromValue(message.getInt(PDU_TYPE, offset)); + } + + /** + * Indicates if this message has a header extension byte + */ + public boolean hasHeaderExtension() + { + return getMessage().get(HEADER_EXTENSION_FLAG + getOffset()); + } + + /** + * Indicates if this is an acknowledgement type PDU + */ + public boolean isAcknowledge() + { + return getMessage().get(ACKNOWLEDGEMENT_FLAG + getOffset()); + } + + /** + * Indicates if this PDU was sent with the priority flag + */ + public boolean isPriority() + { + return getMessage().get(PRIORITY_FLAG + getOffset()); + } + + /** + * Indicates if this packet is a control packet (true) or a user packet (false). + */ + public boolean isControl() + { + return getMessage().get(CONTROL_USER_FLAG + getOffset()); + } + + /** + * Length of the payload + * @return + */ + public int getPayloadLength() + { + return getMessage().getInt(LENGTH, getOffset()); + } + + /** + * Length of this header in bits + * + * Note this length value includes the first 2 bytes of the message that indicates the overall + * message length and the length of the PDU header byte(s). + * + * This method may need to be overridden if there is more than 1 extension header in a PDU. + */ + @Override + public int getLength() + { + if(hasHeaderExtension()) + { + return 16 + 16; + } + else + { + return 16 + 8; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeaderFactory.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeaderFactory.java new file mode 100644 index 00000000..1bf7f985 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeaderFactory.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class ARSHeaderFactory +{ + public static ARSHeader create(ARSPDUType pduType, BinaryMessage message, int offset) + { + switch(pduType) + { + case DEVICE_REGISTRATION: + return new DeviceRegistration(message, offset); + case DEVICE_DEREGISTRATION: + return new DeviceDeRegistration(message, offset); + case QUERY: + return new QueryMessage(message, offset); + case USER_REGISTRATION: + return new UserRegistration(message, offset); + case USER_DEREGISTRATION: + return new UserDeRegistration(message, offset); + case USER_REGISTRATION_ACKNOWLEDGEMENT: + return new UserRegistrationAcknowledge(message, offset); + case REGISTRATION_ACKNOWLEDGEMENT: + return new RegistrationAcknowledgement(message, offset); + case UNKNOWN: + default: + return new ARSHeader(message, offset); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPDUType.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPDUType.java new file mode 100644 index 00000000..9137d4b8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPDUType.java @@ -0,0 +1,63 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +public enum ARSPDUType +{ + DEVICE_REGISTRATION(0x0), + DEVICE_DEREGISTRATION(0x1), + QUERY(0x4), + USER_REGISTRATION(0x5), + USER_DEREGISTRATION(0x6), + USER_REGISTRATION_ACKNOWLEDGEMENT(0x7), + REGISTRATION_ACKNOWLEDGEMENT(0xF), + UNKNOWN(-1); + + private int mValue; + + ARSPDUType(int value) + { + mValue = value; + } + + public static ARSPDUType fromValue(int value) + { + switch(value) + { + case 0x0: + return DEVICE_REGISTRATION; + case 0x1: + return DEVICE_DEREGISTRATION; + case 0x4: + return QUERY; + case 0x5: + return USER_REGISTRATION; + case 0x6: + return USER_DEREGISTRATION; + case 0x7: + return USER_REGISTRATION_ACKNOWLEDGEMENT; + case 0xF: + return REGISTRATION_ACKNOWLEDGEMENT; + } + + return UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPacket.java new file mode 100644 index 00000000..5f0d5bfa --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSPacket.java @@ -0,0 +1,67 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.ip.IPacket; +import io.github.dsheirer.module.decode.ip.Packet; + +public class ARSPacket extends Packet +{ + private ARSHeader mHeader; + + /** + * Constructs a parser for a header contained within a binary message starting at the offset. + * + * @param message containing the packet + * @param offset to the packet within the message + */ + public ARSPacket(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ARS ").append(getHeader()); + return sb.toString(); + } + + @Override + public ARSHeader getHeader() + { + if(mHeader == null) + { + ARSPDUType pduType = ARSHeader.getPDUType(getMessage(), getOffset()); + mHeader = ARSHeaderFactory.create(pduType, getMessage(), getOffset()); + } + + return mHeader; + } + + @Override + public IPacket getPayload() + { + //There are no payloads for ARS packets ... the header parses all of the information. + return null; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceDeRegistration.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceDeRegistration.java new file mode 100644 index 00000000..361211b3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceDeRegistration.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class DeviceDeRegistration extends ARSHeader +{ + /** + * 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 DeviceDeRegistration(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ARS DEVICE/USER DE-REGISTRATION"); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistration.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistration.java new file mode 100644 index 00000000..8d3271ce --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistration.java @@ -0,0 +1,153 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class DeviceRegistration extends ARSHeader +{ + private static final int SECOND_HEADER_EXTENSION_FLAG = 24; + private static final int[] EVENT = {25, 26}; + private static final int[] ENCODING = {27, 28, 29, 30, 31}; + private static final int DEVICE_IDENTIFIER_START = 24; + private static final int DEVICE_IDENTIFIER_START_EXTENDED_HEADER = 32; + + private static final int[] BYTE_VALUE = {0, 1, 2, 3, 4, 5, 6, 7}; + + /** + * 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 DeviceRegistration(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("DEVICE REGISTRATION"); + + if(isValid()) + { + if(hasHeaderExtension()) + { + sb.append(isInitialEvent() ? "-INITIAL " : "-REFRESH "); + } + + sb.append(" ").append(getPayload()); + } + else + { + sb.append(" - ERROR INVALID MESSAGE LENGTH"); + } + return sb.toString(); + } + + /** + * Indicates if this is an initial registration event (true) or refresh event (false) + */ + public boolean isInitialEvent() + { + return getMessage().getInt(EVENT, getOffset()) == 1; + } + + /** + * Device, user and password values contained in the registration packet. + */ + public String getPayload() + { + StringBuilder sb = new StringBuilder(); + sb.append("DEVICE:"); + + int pointer = getOffset(); + + if(hasHeaderExtension()) + { + pointer += DEVICE_IDENTIFIER_START_EXTENDED_HEADER; + } + else + { + pointer += DEVICE_IDENTIFIER_START; + } + + int identifierSize = getMessage().getInt(BYTE_VALUE, pointer += 8); + + if(identifierSize > 0) + { + for(int x = 0; x < identifierSize; x++) + { + sb.append(getCharacter(pointer += 8)); + } + } + else + { + sb.append("(none)"); + } + + sb.append(" USER:"); + + int userIdentifierSize = getMessage().getInt(BYTE_VALUE, pointer += 8); + + if(userIdentifierSize > 0) + { + for(int x = 0; x < userIdentifierSize; x++) + { + sb.append(getCharacter(pointer += 8)); + } + } + else + { + sb.append("(none)"); + } + + sb.append(" PASSWORD:"); + + int passwordSize = getMessage().getInt(BYTE_VALUE, pointer += 8); + + if(passwordSize > 0) + { + for(int x = 0; x < passwordSize; x++) + { + sb.append(getCharacter(pointer += 8)); + } + } + else + { + sb.append("(none)"); + } + + return sb.toString(); + } + + /** + * Returns a UTF-8 encoded character that starts at the specified offset + * + * @param offset of the start of the 8-bit UTF-8 encoded character + * @return character + */ + private char getCharacter(int offset) + { + return (char)getMessage().getByte(BYTE_VALUE, offset); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistrationAcknowledge.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistrationAcknowledge.java new file mode 100644 index 00000000..4f24320a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/DeviceRegistrationAcknowledge.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class DeviceRegistrationAcknowledge extends ARSHeader +{ + /** + * 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 DeviceRegistrationAcknowledge(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("DEVICE REGISTRATION ACKNOWLEDGED"); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/QueryMessage.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/QueryMessage.java new file mode 100644 index 00000000..e1c08371 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/QueryMessage.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class QueryMessage extends ARSHeader +{ + /** + * 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 QueryMessage(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ARS QUERY MESSAGE"); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/RegistrationAcknowledgement.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/RegistrationAcknowledgement.java new file mode 100644 index 00000000..6259d20c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/RegistrationAcknowledgement.java @@ -0,0 +1,96 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class RegistrationAcknowledgement extends ARSHeader +{ + private static final int[] TIMER_OR_REASON = {25, 26, 27, 28, 29, 30, 31}; + + /** + * 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 RegistrationAcknowledgement(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(isValid()) + { + sb.append("REGISTRATION "); + + if(isSuccessful()) + { + sb.append("SUCCESS"); + + int units = getPayloadValue(); + + sb.append(" REFRESH IN:").append(units * 30).append("mins"); + } + else + { + sb.append("FAIL"); + + int units = getPayloadValue(); + + if(units == 0) + { + sb.append(" - UNAUTHORIZED DEVICE"); + } + else + { + sb.append(" REASON:").append(units); + } + } + } + else + { + sb.append("REGISTRATION ACKNOWLEDGE - ERROR - INVALID MESSAGE LENGTH"); + } + + return sb.toString(); + } + + /** + * Indicates if the registration succeeded (true) or failed (false) + */ + public boolean isSuccessful() + { + return !isAcknowledge(); + } + + /** + * Payload value. If successful, this is the number of 30 minute units until a registration + * refresh is required. If unsuccessful, this is the reason (0-unauthorized device). + */ + public int getPayloadValue() + { + return getMessage().getInt(TIMER_OR_REASON, getOffset()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistration.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistration.java new file mode 100644 index 00000000..880994fe --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistration.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class UserDeRegistration extends ARSHeader +{ + /** + * 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 UserDeRegistration(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ARS USER DE-REGISTRATION"); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistrationAcknowledge.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistrationAcknowledge.java new file mode 100644 index 00000000..0cb2e478 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserDeRegistrationAcknowledge.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class UserDeRegistrationAcknowledge extends ARSHeader +{ + /** + * 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 UserDeRegistrationAcknowledge(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ARS USER DE-REGISTRATION ACKNOWLEDGED"); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistration.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistration.java new file mode 100644 index 00000000..8208d8da --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistration.java @@ -0,0 +1,140 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class UserRegistration extends ARSHeader +{ + private static final int HEADER_EXTENSION_FLAG = 24; + private static final int[] EVENT = {25, 26}; + private static final int[] ENCODING = {27, 28, 29, 30, 31}; + private static final int DEVICE_IDENTIFIER_START = 24; + private static final int DEVICE_IDENTIFIER_START_EXTENDED_HEADER = 32; + + private static final int[] BYTE_VALUE = {0, 1, 2, 3, 4, 5, 6, 7}; + + /** + * 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 UserRegistration(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("USER REGISTRATION"); + + if(isValid()) + { + sb.append(" ").append(getPayload()); + } + else + { + sb.append(" - ERROR INVALID MESSAGE LENGTH"); + } + return sb.toString(); + } + + /** + * Device, user and password values contained in the registration packet. + */ + public String getPayload() + { + StringBuilder sb = new StringBuilder(); + sb.append("DEVICE:"); + + int pointer = getOffset(); + + if(hasHeaderExtension()) + { + pointer += DEVICE_IDENTIFIER_START_EXTENDED_HEADER; + } + else + { + pointer += DEVICE_IDENTIFIER_START; + } + + int identifierSize = getMessage().getInt(BYTE_VALUE, pointer += 8); + + if(identifierSize > 0) + { + for(int x = 0; x < identifierSize; x++) + { + sb.append(getCharacter(pointer += 8)); + } + } + else + { + sb.append("(none)"); + } + + sb.append(" USER:"); + + int userIdentifierSize = getMessage().getInt(BYTE_VALUE, pointer += 8); + + if(userIdentifierSize > 0) + { + for(int x = 0; x < userIdentifierSize; x++) + { + sb.append(getCharacter(pointer += 8)); + } + } + else + { + sb.append("(none)"); + } + + sb.append(" PASSWORD:"); + + int passwordSize = getMessage().getInt(BYTE_VALUE, pointer += 8); + + if(passwordSize > 0) + { + for(int x = 0; x < passwordSize; x++) + { + sb.append(getCharacter(pointer += 8)); + } + } + else + { + sb.append("(none)"); + } + + return sb.toString(); + } + + /** + * Returns a UTF-8 encoded character that starts at the specified offset + * + * @param offset of the start of the 8-bit UTF-8 encoded character + * @return character + */ + private char getCharacter(int offset) + { + return (char)getMessage().getByte(BYTE_VALUE, offset); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistrationAcknowledge.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistrationAcknowledge.java new file mode 100644 index 00000000..a3178957 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/UserRegistrationAcknowledge.java @@ -0,0 +1,88 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ars; + +import io.github.dsheirer.bits.BinaryMessage; + +public class UserRegistrationAcknowledge extends ARSHeader +{ + private static final int[] SESSION_TIME_OR_REASON = {25, 26, 27, 28, 29, 30, 31}; + + /** + * 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 UserRegistrationAcknowledge(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + if(isSuccessful()) + { + sb.append("USER REGISTRATION SUCCESS - SESSION TIME:").append(getSessionTime()); + } + else + { + sb.append("USER REGISTRATION FAILED - REASON:").append(getFailureReason()); + } + return sb.toString(); + } + + public boolean isSuccessful() + { + return !isAcknowledge(); + } + + public int getSessionTime() + { + if(hasHeaderExtension()) + { + return getMessage().getInt(SESSION_TIME_OR_REASON, getOffset()); + } + + return 0; + } + + public String getFailureReason() + { + if(hasHeaderExtension()) + { + int reason = getMessage().getInt(SESSION_TIME_OR_REASON, getOffset()); + + switch(reason) + { + case 0x01: + return "USER VALIDATION FAILED"; + case 0x02: + return "USER VALIDATION TIMEOUT"; + default: + return "TRANSMISSION FAILURE"; + } + } + + return "NONE"; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Header.java b/src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Header.java new file mode 100644 index 00000000..d05c8c11 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Header.java @@ -0,0 +1,161 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ipv4; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.ipv4.IPV4Identifier; +import io.github.dsheirer.module.decode.ip.Header; +import io.github.dsheirer.module.decode.ip.IPProtocol; +import io.github.dsheirer.module.decode.p25.identifier.ipv4.APCO25IpAddress; + +public class IPV4Header extends Header +{ + private static final int[] VERSION = {0, 1, 2, 3}; + private static final int[] HEADER_LENGTH = {4, 5, 6, 7}; + private static final int[] TOTAL_LENGTH = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] PROTOCOL = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] FROM_ADDRESS = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + private static final int[] TO_ADDRESS = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159}; + + private IPV4Identifier mFromAddress; + private IPV4Identifier mToAddress; + + public IPV4Header(BinaryMessage message, int offset) + { + super(message, offset); + checkValid(); + } + + /** + * Performs simple header and packet length validations relative to the packet offset within the binary message. + */ + private void checkValid() + { + int headerLength = getLength(); + + if(headerLength < 40) + { + setValid(false); + return; + } + + if(getMessage().size() < headerLength + getOffset()) + { + setValid(false); + return; + } + + int totalLength = getTotalLength(); + + if(totalLength < headerLength) + { + setValid(false); + return; + } + + if(getMessage().size() < totalLength + getOffset()) + { + setValid(false); + return; + } + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("IP FROM:").append(getFromAddress()); + sb.append(" TO:").append(getToAddress()); + return sb.toString(); + } + + /** + * Determines the IP version of the packet that starts at the indicated offset within the binary message. + * + * @param message message containing an IP packet + * @param offset to the start of the packet within the binary message + * @return + */ + public static int getIPVersion(BinaryMessage message, int offset) + { + return message.getInt(VERSION, offset); + } + + /** + * IP Version of this packet + */ + public int getIPVersion() + { + return getIPVersion(getMessage(), getOffset()); + } + + /** + * Length of this IPV4 header in bits (NOT BYTES) + */ + public int getLength() + { + return getMessage().getInt(HEADER_LENGTH, getOffset()) * 32; + } + + /** + * Total Length of this IPV4 packet in bits (NOT BYTES) + */ + public int getTotalLength() + { + return getMessage().getInt(TOTAL_LENGTH, getOffset()) * 8; + } + + /** + * Protocol for the payload being carried by this IP packet + */ + public IPProtocol getProtocol() + { + return IPProtocol.fromValue(getMessage().getInt(PROTOCOL, getOffset())); + } + + /** + * From IP Address + */ + public IPV4Identifier getFromAddress() + { + if(mFromAddress == null) + { + mFromAddress = APCO25IpAddress.createFrom(getMessage().getInt(FROM_ADDRESS, getOffset())); + } + + return mFromAddress; + } + + /** + * To IP Address + */ + public IPV4Identifier getToAddress() + { + if(mToAddress == null) + { + mToAddress = APCO25IpAddress.createTo(getMessage().getInt(TO_ADDRESS, getOffset())); + } + + return mToAddress; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Packet.java b/src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Packet.java new file mode 100644 index 00000000..33eeb32e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ipv4/IPV4Packet.java @@ -0,0 +1,112 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.ipv4; + +import io.github.dsheirer.bits.BinaryMessage; +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; + +/** + * IPV4 packet + */ +public class IPV4Packet extends Packet +{ + private IPV4Header mHeader; + private IPacket mPayload; + + /** + * Constructs an IPV4 packet parser + * + * @param message containing an IPV4 packet + * @param offset to the start of the packet within the binary message + */ + public IPV4Packet(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + if(getHeader().isValid()) + { + sb.append(getHeader()); + } + else + { + sb.append("IPV4 - INVALID HEADER"); + sb.append(" HDR:").append(getHeader().getMessage().toHexString()); + } + + if(hasPayload()) + { + if(getPayload() instanceof UnknownPacket) + { + sb.append(" PROTOCOL:").append(getHeader().getProtocol()); + } + + sb.append(" ").append(getPayload().toString()); + } + else + { + sb.append(" NO PAYLOAD!"); + } + + return sb.toString(); + } + + @Override + public IPV4Header getHeader() + { + if(mHeader == null) + { + mHeader = new IPV4Header(getMessage(), getOffset()); + } + + return mHeader; + } + + /** + * Payload packet for this IP packet. + * + * @return payload packet or null + */ + @Override + public IPacket getPayload() + { + if(mPayload == null) + { + if(getHeader().isValid()) + { + mPayload = PacketMessageFactory.create(getHeader().getProtocol(), getMessage(), + getOffset() + getHeader().getLength()); + } + else + { + mPayload = new UnknownPacket(getMessage(), getOffset()); + } + } + + return mPayload; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPHeader.java new file mode 100644 index 00000000..98376267 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPHeader.java @@ -0,0 +1,78 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.udp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.ip.Header; + +public class UDPHeader extends Header +{ + private static final int[] SOURCE_PORT = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] DESTINATION_PORT = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + /** + * 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 UDPHeader(BinaryMessage message, int offset) + { + super(message, offset); + checkValid(); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("UDP PORT FROM:").append(getSourcePort()); + sb.append(" TO:").append(getDestinationPort()); + return sb.toString(); + } + + private void checkValid() + { + setValid(getMessage().size() >= getOffset() + getLength()); + } + + @Override + public int getLength() + { + return 64; + } + + /** + * Source UDP port number + * @return + */ + public int getSourcePort() + { + return getMessage().getInt(SOURCE_PORT, getOffset()); + } + + /** + * Destination UDP port number + */ + public int getDestinationPort() + { + return getMessage().getInt(DESTINATION_PORT, getOffset()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPPacket.java new file mode 100644 index 00000000..570e693d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/udp/UDPPacket.java @@ -0,0 +1,88 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.udp; + +import io.github.dsheirer.bits.BinaryMessage; +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; + +public class UDPPacket extends Packet +{ + private UDPHeader mHeader; + private IPacket mPayload; + + /** + * Constructs a parser for a header contained within a binary message starting at the offset. + * + * @param message containing the packet + * @param offset to the packet within the message + */ + public UDPPacket(BinaryMessage message, int offset) + { + super(message, offset); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + if(getHeader().isValid()) + { + sb.append(getHeader()); + } + else + { + sb.append("INVALID HEADER"); + } + + sb.append(" ").append(getPayload()); + + return sb.toString(); + } + + @Override + public UDPHeader getHeader() + { + if(mHeader == null) + { + mHeader = new UDPHeader(getMessage(), getOffset()); + } + + return mHeader; + } + + @Override + public IPacket getPayload() + { + if(mPayload == null && getHeader().isValid()) + { + 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); + } + + return mPayload; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCode.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCode.java new file mode 100644 index 00000000..b45e6645 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCode.java @@ -0,0 +1,52 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.lj1200; + +/** + * LoJack Function and Reply Code + */ +public class FunctionAndReplyCode +{ + private LJ1200Message.Function mFunction; + private String mReplyCode; + + public FunctionAndReplyCode(LJ1200Message.Function function, String replyCode) + { + mFunction = function; + mReplyCode = replyCode; + } + + public LJ1200Message.Function getFunction() + { + return mFunction; + } + + public String getReplyCode() + { + return mReplyCode; + } + + @Override + public String toString() + { + return "FUNCTION:" + mFunction + " REPLY CODE:" + mReplyCode; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCodeIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCodeIdentifier.java new file mode 100644 index 00000000..1372e886 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/FunctionAndReplyCodeIdentifier.java @@ -0,0 +1,46 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.lj1200; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.protocol.Protocol; + +public class FunctionAndReplyCodeIdentifier extends Identifier +{ + public FunctionAndReplyCodeIdentifier(FunctionAndReplyCode functionAndReplyCode) + { + super(functionAndReplyCode, IdentifierClass.NETWORK, Form.LOJACK, Role.TO); + } + + @Override + public Protocol getProtocol() + { + return Protocol.LOJACK; + } + + public static FunctionAndReplyCodeIdentifier create(LJ1200Message.Function function, String replyCode) + { + return new FunctionAndReplyCodeIdentifier(new FunctionAndReplyCode(function, replyCode)); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200CallEvent.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200CallEvent.java index c90d3df1..15713a3b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200CallEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200CallEvent.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.lj1200; import io.github.dsheirer.alias.Alias; @@ -24,36 +26,26 @@ import io.github.dsheirer.module.decode.event.CallEvent; public class LJ1200CallEvent extends CallEvent { - public LJ1200CallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String details ) + public LJ1200CallEvent(CallEventType callEventType, AliasList aliasList, String fromID, String toID, String details) { - super( DecoderType.LJ_1200, callEventType, - aliasList, fromID, toID, details ); + super(DecoderType.LJ_1200, callEventType, aliasList, fromID, toID, details); } - private Alias getAlias( String ident ) + private Alias getAlias(String ident) { - if( hasAliasList() ) - { - return getAliasList().getFleetsyncAlias( ident ); - } - return null; } - + @Override public Alias getFromIDAlias() { - return getAlias( getFromID() ); + return getAlias(getFromID()); } @Override public Alias getToIDAlias() { - return getAlias( getToID() ); + return getAlias(getToID()); } @Override @@ -79,30 +71,30 @@ public class LJ1200CallEvent extends CallEvent private String mToID; private String mDetails; - public Builder( CallEventType callEventType ) + public Builder(CallEventType callEventType) { mCallEventType = callEventType; } - - public Builder aliasList( AliasList aliasList ) + + public Builder aliasList(AliasList aliasList) { mAliasList = aliasList; return this; } - - public Builder from( String val ) + + public Builder from(String val) { mFromID = val; return this; -} + } - public Builder details( String details ) + public Builder details(String details) { mDetails = details; return this; } - - public Builder to( String toID ) + + public Builder to(String toID) { mToID = toID; return this; @@ -110,29 +102,25 @@ public class LJ1200CallEvent extends CallEvent public LJ1200CallEvent build() { - return new LJ1200CallEvent( this ); + return new LJ1200CallEvent(this); } } /** * Private constructor for the builder */ - private LJ1200CallEvent( Builder builder ) + private LJ1200CallEvent(Builder builder) { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mDetails ); + this(builder.mCallEventType, + builder.mAliasList, + builder.mFromID, + builder.mToID, + builder.mDetails); } - - public static LJ1200CallEvent getLJ1200Event( LJ1200Message message ) + + public static LJ1200CallEvent getLJ1200Event(LJ1200Message message) { CallEventType type = CallEventType.COMMAND; - - return new LJ1200CallEvent.Builder( type ) - .to( message.getAddress() ) - .details( message.getMessage() ) - .build(); + return new LJ1200CallEvent.Builder(type).to(message.getAddress()).details(message.toString()).build(); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java index 62842393..a942b6aa 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.lj1200; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.IBinarySymbolProcessor; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.bits.SyncPattern; @@ -35,26 +38,26 @@ public class LJ1200Decoder extends AbstractAFSKDecoder implements IBinarySymbolP private MessageFramer mTransponderMessageFramer; private LJ1200MessageProcessor mMessageProcessor; - protected LJ1200Decoder(AFSK1200Decoder decoder, AliasList aliasList) + protected LJ1200Decoder(AFSK1200Decoder decoder) { super(decoder); - init(aliasList); + init(); } - public LJ1200Decoder(AliasList aliasList) + public LJ1200Decoder() { super(AFSK1200Decoder.Output.NORMAL); - init(aliasList); + init(); } - private void init(AliasList aliasList) + private void init() { getDecoder().setSymbolProcessor(this); mTowerMessageFramer = new MessageFramer(SyncPattern.LJ1200.getPattern(), MESSAGE_LENGTH); mTransponderMessageFramer = new MessageFramer(SyncPattern.LJ1200_TRANSPONDER.getPattern(), MESSAGE_LENGTH); - mMessageProcessor = new LJ1200MessageProcessor(aliasList); + mMessageProcessor = new LJ1200MessageProcessor(); mTowerMessageFramer.addMessageListener(mMessageProcessor); mTransponderMessageFramer.addMessageListener(mMessageProcessor); mMessageProcessor.setMessageListener(getMessageListener()); @@ -74,10 +77,10 @@ public class LJ1200Decoder extends AbstractAFSKDecoder implements IBinarySymbolP } @Override - public void receive(boolean symbol) + public void process(boolean symbol) { - mTowerMessageFramer.receive(symbol); - mTransponderMessageFramer.receive(symbol); + mTowerMessageFramer.process(symbol); + mTransponderMessageFramer.process(symbol); } public MessageFramer getTowerMessageFramer() diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderInstrumented.java index 735dd2d7..c92aeee6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderInstrumented.java @@ -29,7 +29,7 @@ public class LJ1200DecoderInstrumented extends LJ1200Decoder implements IInstrum public LJ1200DecoderInstrumented() { - super(new AFSK1200DecoderInstrumented(false), null); + super(new AFSK1200DecoderInstrumented(false)); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java index e479c434..18b156d6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,20 +15,19 @@ * * 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.lj1200; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.module.decode.event.DecodeEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,14 +43,8 @@ public class LJ1200DecoderState extends DecoderState private Set mAddresses = new TreeSet(); - private AliasedStringAttributeMonitor mAddressAttribute; - - public LJ1200DecoderState(AliasList aliasList) + public LJ1200DecoderState() { - super(aliasList); - - mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE); } @Override @@ -60,27 +54,49 @@ public class LJ1200DecoderState extends DecoderState } @Override - public void receive(Message message) + public void receive(IMessage message) { if(message instanceof LJ1200Message) { - LJ1200Message lj = (LJ1200Message) message; + LJ1200Message lj = (LJ1200Message)message; if(lj.isValid()) { String address = lj.getAddress(); - mAddressAttribute.process(address); mAddresses.add(address); - broadcast(LJ1200CallEvent.getLJ1200Event(lj)); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(lj.getIdentifiers()); + DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis()) + .identifiers(ic) + .channel(getCurrentChannel()) + .details("LOJACK") + .eventDescription(lj.toString()) + .build(); + + broadcast(event); broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); } } else if(message instanceof LJ1200TransponderMessage) { + LJ1200TransponderMessage transponder = (LJ1200TransponderMessage)message; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(transponder.getIdentifiers()); + + DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis()) + .identifiers(ic) + .channel(getCurrentChannel()) + .details("LOJACK TRANSPONDER") + .eventDescription(transponder.toString()) + .build(); + + broadcast(transponderEvent); } } @@ -120,9 +136,9 @@ public class LJ1200DecoderState extends DecoderState } } - private void resetState() + protected void resetState() { - mAddressAttribute.reset(); + super.resetState(); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Message.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Message.java index 962d1a82..607894ec 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Message.java @@ -1,29 +1,30 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014,2015 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.lj1200; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; import io.github.dsheirer.edac.CRC; import io.github.dsheirer.edac.CRCLJ; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.esn.ESNIdentifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,388 +33,305 @@ import java.util.List; public class LJ1200Message extends Message { - private final static Logger mLog = LoggerFactory.getLogger( LJ1200Message.class ); - - public static final String[] REPLY_CODE = { "0","1","2","3","4","5","6","7", - "8","9","A","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S", - "T","U","V","W","X","Y" }; + private final static Logger mLog = LoggerFactory.getLogger(LJ1200Message.class); - public static int[] SYNC = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }; + public static final String[] REPLY_CODE = {"0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "A", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", + "T", "U", "V", "W", "X", "Y"}; - public static int[] VRC = { 23,22,21,20,19,18,17,16 }; - - public static int[] LRC = { 31,30,29,28,27,26,25,24 }; + public static int[] SYNC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + public static int[] VRC = {23, 22, 21, 20, 19, 18, 17, 16}; + public static int[] LRC = {31, 30, 29, 28, 27, 26, 25, 24}; + public static int[] FUNCTION = {35, 34, 33, 32}; + public static int[] ADDRESS = {63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, + 42, 41, 40, 39, 38, 37, 36}; + public static int[] REPLY_1 = {39, 38, 37, 36, 43}; + public static int[] REPLY_2 = {42, 41, 40, 47, 46}; + public static int[] REPLY_3 = {45, 44, 51, 50, 49}; + public static int[] REPLY_4 = {48, 55, 54, 53, 52}; + public static int[] REPLY_5 = {59, 58, 57, 56, 63}; - public static int[] FUNCTION = { 35,34,33,32 }; + public static int[] MESSAGE_CRC = {79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64}; - public static int[] ADDRESS = { 63,62,61,60,59,58,57,56,55,54,53,52,51,50, - 49,48,47,46,45,44,43,42,41,40,39,38,37,36 }; - - public static int[] REPLY_1 = { 39,38,37,36,43 }; - public static int[] REPLY_2 = { 42,41,40,47,46 }; - public static int[] REPLY_3 = { 45,44,51,50,49 }; - public static int[] REPLY_4 = { 48,55,54,53,52 }; - public static int[] REPLY_5 = { 59,58,57,56,63 }; - - public static int[] MESSAGE_CRC = { 79,78,77,76,75,74,73,72,71,70,69,68,67, - 66,65,64 }; - private BinaryMessage mMessage; - private AliasList mAliasList; private CRC mCRC; - - public LJ1200Message( BinaryMessage message, AliasList list ) + private FunctionAndReplyCodeIdentifier mFunctionAndReplyCodeIdentifier; + private ESNIdentifier mTransponderIdentifier; + private List mIdentifiers; + + public LJ1200Message(BinaryMessage message) { - mMessage = message; - mAliasList = list; - + mMessage = message; + checkCRC(); - switch( mCRC ) + switch(mCRC) { - case CORRECTED: - mLog.debug( "CORR:" + message.toString() ); - break; - case FAILED_CRC: - mLog.debug( "FAIL:" + message.toString() ); - break; - case PASSED: - mLog.debug( "PASS:" + message.toString() ); - break; - default: - break; + case CORRECTED: + mLog.debug("CORR:" + message.toString()); + break; + case FAILED_CRC: + mLog.debug("FAIL:" + message.toString()); + break; + case PASSED: + mLog.debug("PASS:" + message.toString()); + break; + default: + break; } } - + private void checkCRC() { - mCRC = CRCLJ.checkAndCorrect( mMessage ); + mCRC = CRCLJ.checkAndCorrect(mMessage); } - + public boolean isValid() { - return mCRC == CRC.PASSED || mCRC == CRC.CORRECTED; + return mCRC == CRC.PASSED || mCRC == CRC.CORRECTED; } - + + public FunctionAndReplyCodeIdentifier getFunctionAndReplyCodeIdentifier() + { + if(mFunctionAndReplyCodeIdentifier == null) + { + mFunctionAndReplyCodeIdentifier = FunctionAndReplyCodeIdentifier.create(getFunction(), getReplyCode()); + } + + return mFunctionAndReplyCodeIdentifier; + } + + public ESNIdentifier getTransponder() + { + if(mTransponderIdentifier == null) + { + mTransponderIdentifier = ESNIdentifier.create(getAddress(), Protocol.LOJACK, Role.TO); + } + + return mTransponderIdentifier; + } + public String getVRC() { - return mMessage.getHex( VRC, 2 ); + return mMessage.getHex(VRC, 2); } - + public String getLRC() { - return mMessage.getHex( LRC, 2 ); + return mMessage.getHex(LRC, 2); } - + public String getCRC() { - return mMessage.getHex( MESSAGE_CRC, 4 ); + return mMessage.getHex(MESSAGE_CRC, 4); } - + public Function getFunction() { - return Function.fromValue( mMessage.getInt( FUNCTION ), - mMessage.getInt( REPLY_3 ) ); + return Function.fromValue(mMessage.getInt(FUNCTION), mMessage.getInt(REPLY_3)); } - + public String getAddress() { - return mMessage.getHex( ADDRESS, 7 ); - } - - public Alias getTransponderAlias() - { - if( mAliasList != null ) - { - return mAliasList.getESNAlias( getAddress() ); - } - - return null; - } - - public Alias getFunctionAndIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getLoJackAlias( getFunction(), getReplyCode() ); - } - - return null; + return mMessage.getHex(ADDRESS, 7); } /** * 5 character reply code for function E and F, transponder activation. + * * @return - reply code */ public String getReplyCode() { - StringBuilder sb = new StringBuilder(); - - sb.append( REPLY_CODE[ mMessage.getInt( REPLY_1 ) ] ); - sb.append( REPLY_CODE[ mMessage.getInt( REPLY_2 ) ] ); - sb.append( REPLY_CODE[ mMessage.getInt( REPLY_3 ) ] ); - sb.append( REPLY_CODE[ mMessage.getInt( REPLY_4 ) ] ); - sb.append( REPLY_CODE[ mMessage.getInt( REPLY_5 ) ] ); - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(REPLY_CODE[mMessage.getInt(REPLY_1)]); + sb.append(REPLY_CODE[mMessage.getInt(REPLY_2)]); + sb.append(REPLY_CODE[mMessage.getInt(REPLY_3)]); + sb.append(REPLY_CODE[mMessage.getInt(REPLY_4)]); + sb.append(REPLY_CODE[mMessage.getInt(REPLY_5)]); + + return sb.toString(); } - + @Override public String toString() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - Function function = getFunction(); - - sb.append( "FUNCTION: " ); - sb.append( function.toString() ); + Function function = getFunction(); - switch( function ) - { - case F1_SITE_ID: - sb.append( " SITE [" ); - break; - case F1_SPEED_UP: - case F2_TEST: - case F3_DEACTIVATE: - case F4_ACTIVATE: - case FF_TRACK_PULSE: - default: - sb.append( " REPLY CODE [" ); - break; - } - - sb.append( getReplyCode() ); + sb.append("FUNCTION: "); + sb.append(function.toString()); - Alias site = getFunctionAndIDAlias(); - - if( site != null ) - { - sb.append( "/" ); - sb.append( site.getName() ); - } - - sb.append( "] ADDRESS [" ); - sb.append( getAddress() ); - sb.append( "] VRC [" + getVRC() ); - sb.append( "] LRC [" + getLRC() ); - sb.append( "] CRC [" + getCRC() ); - sb.append( "]" ); + switch(function) + { + case F1_SITE_ID: + sb.append(" SITE ["); + break; + case F1_SPEED_UP: + case F2_TEST: + case F3_DEACTIVATE: + case F4_ACTIVATE: + case FF_TRACK_PULSE: + default: + sb.append(" REPLY CODE ["); + break; + } - return sb.toString(); + sb.append(getReplyCode()); + + sb.append("] ADDRESS ["); + sb.append(getAddress()); + sb.append("] VRC [" + getVRC()); + sb.append("] LRC [" + getLRC()); + sb.append("] CRC [" + getCRC()); + sb.append("]"); + + return sb.toString(); } /** * Pads spaces onto the end of the value to make it 'places' long */ - public String pad( String value, int places, String padCharacter ) + public String pad(String value, int places, String padCharacter) { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(value); + + while(sb.length() < places) + { + sb.append(padCharacter); + } + + return sb.toString(); } - + /** * Pads an integer value with additional zeroes to make it decimalPlaces long */ - public String format( int number, int decimalPlaces ) + public String format(int number, int decimalPlaces) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); + int paddingRequired = decimalPlaces - (String.valueOf(number).length()); - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } + for(int x = 0; x < paddingRequired; x++) + { + sb.append("0"); + } - @Override - public String getProtocol() - { - return "LJ-1200"; + sb.append(number); + + return sb.toString(); } - @Override - public String getEventType() + @Override + public Protocol getProtocol() { - if( getFunction() == Function.F1_SPEED_UP ) - { - return "TRANSPONDER"; - } - - return "TOWER"; + return Protocol.LOJACK; } - @Override - public String getFromID() + public enum Function { - return getReplyCode(); + /* Big Endian Format */ + F0_UNKNOWN("0-UNKNOWN"), + F1_SITE_ID("1Y-SITE ID"), + F1_SPEED_UP("1-SPEED UP"), + F2_TEST("2-TOWER TEST"), + F3_DEACTIVATE("3-DEACTIVATE"), + F4_ACTIVATE("4-ACTIVATE"), + F5_TRANSPONDER_TEST_REPLY("5-TRANSPONDER TEST REPLY"), + F6_TRANSPONDER_TEST_COMMAND("6-TRANSPONDER TEST COMMAND"), + F7_QUIET_COMMAND("7-QUIET COMMAND"), + F8_UNKNOWN("8-UNKNOWN"), + F9_UNKNOWN("9-UNKNOWN"), + FA_UNKNOWN("A-UNKNOWN"), + FB_UNKNOWN("B-UNKNOWN"), + FC_ALT_DEACTIVATE("C-ALT-DEACTIVATE"), + FD_UNKNOWN("D-UNKNOWN"), + FE_UNKNOWN("E-UNKNOWN"), + FF_TRACK_PULSE("F-TRACK PULSE"), + + UNKNOWN("UNKNOWN"); + + private String mLabel; + + private Function(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } + + public static Function fromValue(int value, int replyCodeDigit3) + { + switch(value) + { + case 0: + return Function.F0_UNKNOWN; + case 1: + if(replyCodeDigit3 == 31) /* 'Y' middle character */ + { + return Function.F1_SITE_ID; + } + else + { + return Function.F1_SPEED_UP; + } + case 2: + return Function.F2_TEST; + case 3: + return Function.F3_DEACTIVATE; + case 4: + return Function.F4_ACTIVATE; + case 5: + return Function.F5_TRANSPONDER_TEST_REPLY; + case 6: + return Function.F6_TRANSPONDER_TEST_COMMAND; + case 7: + return Function.F7_QUIET_COMMAND; + case 8: + return Function.F8_UNKNOWN; + case 9: + return Function.F9_UNKNOWN; + case 10: + return Function.FA_UNKNOWN; + case 11: + return Function.FB_UNKNOWN; + case 12: + return Function.FC_ALT_DEACTIVATE; + case 13: + return Function.FD_UNKNOWN; + case 14: + return Function.FE_UNKNOWN; + case 15: + return Function.FF_TRACK_PULSE; + default: + return Function.UNKNOWN; + } + } } - @Override - public Alias getFromIDAlias() + @Override + public List getIdentifiers() { - return getFunctionAndIDAlias(); - } + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getFunctionAndReplyCodeIdentifier()); + mIdentifiers.add(getTransponder()); + } - @Override - public String getToID() - { - return getAddress(); - } - - @Override - public Alias getToIDAlias() - { - return null; + return mIdentifiers; } - - @Override - public String getMessage() - { - return toString(); - } - - @Override - public String getErrorStatus() - { - return mCRC.getDisplayText(); - } - - @Override - public Plottable getPlottable() - { - return null; - } - - public enum Function - { - /* Big Endian Format */ - F0_UNKNOWN( "0-UNKNOWN" ), - F1_SITE_ID( "1Y-SITE ID" ), - F1_SPEED_UP( "1-SPEED UP" ), - F2_TEST( "2-TOWER TEST" ), - F3_DEACTIVATE( "3-DEACTIVATE" ), - F4_ACTIVATE( "4-ACTIVATE" ), - F5_TRANSPONDER_TEST_REPLY( "5-TRANSPONDER TEST REPLY" ), - F6_TRANSPONDER_TEST_COMMAND( "6-TRANSPONDER TEST COMMAND" ), - F7_QUIET_COMMAND( "7-QUIET COMMAND" ), - F8_UNKNOWN( "8-UNKNOWN" ), - F9_UNKNOWN( "9-UNKNOWN" ), - FA_UNKNOWN( "A-UNKNOWN" ), - FB_UNKNOWN( "B-UNKNOWN" ), - FC_ALT_DEACTIVATE( "C-ALT-DEACTIVATE" ), - FD_UNKNOWN( "D-UNKNOWN" ), - FE_UNKNOWN( "E-UNKNOWN" ), - FF_TRACK_PULSE( "F-TRACK PULSE" ), - - UNKNOWN( "UNKNOWN" ); - - private String mLabel; - - private Function( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } - - public static Function fromValue( int value, int replyCodeDigit3 ) - { - switch( value ) - { - case 0: - return Function.F0_UNKNOWN; - case 1: - if( replyCodeDigit3 == 31 ) /* 'Y' middle character */ - { - return Function.F1_SITE_ID; - } - else - { - return Function.F1_SPEED_UP; - } - case 2: - return Function.F2_TEST; - case 3: - return Function.F3_DEACTIVATE; - case 4: - return Function.F4_ACTIVATE; - case 5: - return Function.F5_TRANSPONDER_TEST_REPLY; - case 6: - return Function.F6_TRANSPONDER_TEST_COMMAND; - case 7: - return Function.F7_QUIET_COMMAND; - case 8: - return Function.F8_UNKNOWN; - case 9: - return Function.F9_UNKNOWN; - case 10: - return Function.FA_UNKNOWN; - case 11: - return Function.FB_UNKNOWN; - case 12: - return Function.FC_ALT_DEACTIVATE; - case 13: - return Function.FD_UNKNOWN; - case 14: - return Function.FE_UNKNOWN; - case 15: - return Function.FF_TRACK_PULSE; - default: - return Function.UNKNOWN; - } - } - } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias functionAndID = getFunctionAndIDAlias(); - - if( functionAndID != null ) - { - aliases.add( functionAndID ); - } - - Alias transponder = getTransponderAlias(); - - if( transponder != null ) - { - aliases.add( transponder ); - } - - return aliases; - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java index 64faf7c9..96c4916f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java @@ -1,17 +1,17 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014,2015 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 ******************************************************************************/ @@ -19,35 +19,34 @@ package io.github.dsheirer.module.decode.lj1200; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import java.util.Collections; import java.util.List; -public class LJ1200MessageFilter extends Filter +public class LJ1200MessageFilter extends Filter { - - public LJ1200MessageFilter() - { - super( "LJ-1200 Message Filter" ); - } - - @Override - public boolean passes( Message message ) + + public LJ1200MessageFilter() { - return mEnabled && canProcess( message ); + super("LJ-1200 Message Filter"); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof LJ1200Message || - message instanceof LJ1200TransponderMessage; + return mEnabled && canProcess(message); } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage; + } + + @Override public List> getFilterElements() { - return Collections.EMPTY_LIST; + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageProcessor.java index 4b212b5e..a3fc2645 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageProcessor.java @@ -1,82 +1,78 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.lj1200; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class LJ1200MessageProcessor implements Listener +public class LJ1200MessageProcessor implements Listener { - private final static Logger mLog = - LoggerFactory.getLogger( LJ1200MessageProcessor.class ); + private final static Logger mLog = LoggerFactory.getLogger(LJ1200MessageProcessor.class); - public static int[] SYNC = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }; + public static int[] SYNC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - public static int SYNC_TOWER = 0x550F; - public static int SYNC_TRANSPONDER = 0x2AD5; + public static int SYNC_TOWER = 0x550F; + public static int SYNC_TRANSPONDER = 0x2AD5; - private Listener mMessageListener; - - private AliasList mAliasList; - - public LJ1200MessageProcessor( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mAliasList = null; - mMessageListener = null; - } - - @Override - public void receive( BinaryMessage buffer ) + private Listener mMessageListener; + + public LJ1200MessageProcessor() { - int sync = buffer.getInt( SYNC ); - - if( sync == SYNC_TOWER ) - { - if( mMessageListener != null ) - { - mMessageListener.receive( new LJ1200Message( buffer, mAliasList ) ); - } - } - else if( sync == SYNC_TRANSPONDER ) - { - if( mMessageListener != null ) - { - mMessageListener.receive( new LJ1200TransponderMessage( buffer, mAliasList ) ); - } - } } - - public void setMessageListener( Listener listener ) + + public void dispose() { - mMessageListener = listener; + mMessageListener = null; + } + + @Override + public void receive(CorrectedBinaryMessage message) + { + int sync = message.getInt(SYNC); + + if(sync == SYNC_TOWER) + { + if(mMessageListener != null) + { + mMessageListener.receive(new LJ1200Message(message)); + } + } + else if(sync == SYNC_TRANSPONDER) + { + if(mMessageListener != null) + { + mMessageListener.receive(new LJ1200TransponderMessage(message)); + } + } + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200TransponderMessage.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200TransponderMessage.java index 2ebd0b70..33654be2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200TransponderMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200TransponderMessage.java @@ -1,194 +1,145 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014,2015 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.lj1200; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.List; public class LJ1200TransponderMessage extends Message { - private final static Logger mLog = LoggerFactory.getLogger( LJ1200TransponderMessage.class ); + private final static Logger mLog = LoggerFactory.getLogger(LJ1200TransponderMessage.class); - public static int[] SYNC = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }; + public static int[] SYNC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - public static int[] MESSAGE_CRC = { 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79 }; - - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); + public static int[] MESSAGE_CRC = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; - private BinaryMessage mMessage; - private AliasList mAliasList; + private static SimpleDateFormat mSDF = new SimpleDateFormat("yyyyMMdd HHmmss"); + + private CorrectedBinaryMessage mMessage; private CRC mCRC; - - public LJ1200TransponderMessage( BinaryMessage message, AliasList list ) + + public LJ1200TransponderMessage(CorrectedBinaryMessage message) { - mMessage = message; - mAliasList = list; - + mMessage = message; + checkCRC(); - switch( mCRC ) + switch(mCRC) { - case CORRECTED: - mLog.debug( "CORR:" + message.toString() ); - break; - case FAILED_CRC: - mLog.debug( "FAIL:" + message.toString() ); - break; - case PASSED: - mLog.debug( "PASS:" + message.toString() ); - break; - case UNKNOWN: - mLog.debug( "UNKNOWN:" + message.toString() ); - break; - default: - break; + case CORRECTED: + mLog.debug("CORR:" + message.toString()); + break; + case FAILED_CRC: + mLog.debug("FAIL:" + message.toString()); + break; + case PASSED: + mLog.debug("PASS:" + message.toString()); + break; + case UNKNOWN: + mLog.debug("UNKNOWN:" + message.toString()); + break; + default: + break; } } - + private void checkCRC() { - mCRC = CRC.UNKNOWN; + mCRC = CRC.UNKNOWN; } - + public boolean isValid() { // return mCRC == CRC.PASSED || mCRC == CRC.CORRECTED; - - /* Override validity check until proper CRC is implemented */ - return true; + + /* Override validity check until proper CRC is implemented */ + return true; } - + public String getCRC() { - return mMessage.getHex( MESSAGE_CRC, 4 ); + return mMessage.getHex(MESSAGE_CRC, 4); } - + @Override public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append( "TRANSPONDER TEST" ); + StringBuilder sb = new StringBuilder(); - return sb.toString(); + sb.append("TRANSPONDER TEST"); + + return sb.toString(); } /** * Pads spaces onto the end of the value to make it 'places' long */ - public String pad( String value, int places, String padCharacter ) + public String pad(String value, int places, String padCharacter) { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(value); + + while(sb.length() < places) + { + sb.append(padCharacter); + } + + return sb.toString(); } - + /** * Pads an integer value with additional zeroes to make it decimalPlaces long */ - public String format( int number, int decimalPlaces ) + public String format(int number, int decimalPlaces) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); + int paddingRequired = decimalPlaces - (String.valueOf(number).length()); - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } + for(int x = 0; x < paddingRequired; x++) + { + sb.append("0"); + } - @Override - public String getProtocol() - { - return "LJ-1200"; + sb.append(number); + + return sb.toString(); } - @Override - public String getEventType() + @Override + public Protocol getProtocol() { - return "TRANSPONDER"; + return Protocol.LOJACK; } - @Override - public String getFromID() + @Override + public List getIdentifiers() { - return null; - } - - @Override - public Alias getFromIDAlias() - { - return null; - } - - @Override - public String getToID() - { - return null; - } - - @Override - public Alias getToIDAlias() - { - return null; - } - - @Override - public String getMessage() - { - return toString(); - } - - @Override - public String getErrorStatus() - { - return mCRC.getDisplayText(); - } - - @Override - public Plottable getPlottable() - { - return null; + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRCallEvent.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRCallEvent.java deleted file mode 100644 index 6ff1d189..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRCallEvent.java +++ /dev/null @@ -1,212 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.ltrnet; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class LTRCallEvent extends CallEvent -{ - private String mChannel; - private long mFrequency; - - public LTRCallEvent( DecoderType type, - CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String channel, - long frequency, - String details ) - { - super( type, callEventType, aliasList, fromID, toID, details ); - - mChannel = channel; - mFrequency = frequency; - } - - /** - * Checks if the message's talkgroup matches this call event. Counts the - * number of messages that match this call event to use in determining if - * a call event is valid or an isolated (errant) message. - * - * See: isValid() - */ - public boolean isMatchingTalkgroup( LTRNetMessage message ) - { - String talkgroup = message.getTalkgroupID(); - - if( talkgroup != null && getToID() != null && - talkgroup.contentEquals( getToID() ) ) - { - return true; - } - - return isMatchingTalkgroup( message.getTalkgroupID() ); - } - - public boolean isMatchingTalkgroup( String talkgroup ) - { - return talkgroup != null && getToID() != null && talkgroup.contentEquals( getToID() ); - } - - private Alias getAlias(String talkgroup ) - { - if( hasAliasList() ) - { - return getAliasList().getTalkgroupAlias( talkgroup ); - } - - return null; - } - - @Override - public Alias getFromIDAlias() - { - if( mCallEventType == CallEventType.CALL || - mCallEventType == CallEventType.REGISTER || - mCallEventType == CallEventType.ID_UNIQUE ) - { - if( hasAliasList() ) - { - try - { - int radioID = Integer.parseInt( getFromID() ); - - return mAliasList.getUniqueID( radioID ); - } - catch( Exception e ) - { - //Do nothing, we couldn't parse the int radio id value - } - } - } - else if( mCallEventType == CallEventType.REGISTER_ESN ) - { - if( hasAliasList() ) - { - return mAliasList.getESNAlias( getFromID() ); - } - } - else - { - return getAlias( getFromID() ); - } - - return null; - } - - @Override - public Alias getToIDAlias() - { - return getAlias( getToID() ); - } - - @Override - public String getChannel() - { - return mChannel; - } - - @Override - public long getFrequency() - { - return mFrequency; - } - - public static class Builder - { - /* Required parameters */ - private DecoderType mDecoderType; - private CallEventType mCallEventType; - - /* Optional parameters */ - private AliasList mAliasList; - private String mFromID; - private String mToID; - private String mDetails; - private String mChannel; - private long mFrequency; - - public Builder( DecoderType decoderType, CallEventType callEventType ) - { - mDecoderType = decoderType; - mCallEventType = callEventType; - } - - public Builder aliasList( AliasList aliasList ) - { - mAliasList = aliasList; - return this; - } - - public Builder from( String val ) - { - mFromID = val; - return this; - } - - public Builder channel( String channel ) - { - mChannel = channel; - return this; - } - - public Builder details( String details ) - { - mDetails = details; - return this; - } - - public Builder frequency( long frequency ) - { - mFrequency = frequency; - return this; - } - - public Builder to( String toID ) - { - mToID = toID; - return this; - } - - public LTRCallEvent build() - { - return new LTRCallEvent( this ); - } - - } - - /** - * Private constructor for the builder - */ - private LTRCallEvent( Builder builder ) - { - this( builder.mDecoderType, - builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mChannel, - builder.mFrequency, - builder.mDetails ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java index 8d749bb5..2a546c92 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java @@ -15,7 +15,6 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.ltrnet; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.bits.SyncPattern; import io.github.dsheirer.dsp.fsk.LTRDecoder; @@ -37,7 +36,7 @@ public class LTRNetDecoder extends Decoder implements IReusableBufferListener, L * LTR-Net Decoder. Decodes unfiltered (e.g. demodulated but with no DC or * audio filtering) samples and produces LTR-Net messages. */ - public LTRNetDecoder(DecodeConfigLTRNet config, AliasList aliasList, LTRDecoder ltrDecoder) + public LTRNetDecoder(DecodeConfigLTRNet config, LTRDecoder ltrDecoder) { mLTRDecoder = ltrDecoder; @@ -52,7 +51,7 @@ public class LTRNetDecoder extends Decoder implements IReusableBufferListener, L mLTRDecoder.setMessageFramer(mLTRMessageFramer); mLTRMessageFramer.setSyncDetectListener(mLTRDecoder); - mLTRMessageProcessor = new LTRNetMessageProcessor(config.getMessageDirection(), aliasList); + mLTRMessageProcessor = new LTRNetMessageProcessor(config.getMessageDirection()); mLTRMessageFramer.addMessageListener(mLTRMessageProcessor); mLTRMessageProcessor.setMessageListener(getMessageListener()); } @@ -61,9 +60,9 @@ public class LTRNetDecoder extends Decoder implements IReusableBufferListener, L * LTR-Net Decoder. Decodes unfiltered (e.g. demodulated but with no DC or * audio filtering) samples and produces LTR-Net messages. */ - public LTRNetDecoder(DecodeConfigLTRNet config, AliasList aliasList) + public LTRNetDecoder(DecodeConfigLTRNet config) { - this(config, aliasList, new LTRDecoder(LTR_NET_MESSAGE_LENGTH)); + this(config, new LTRDecoder(LTR_NET_MESSAGE_LENGTH)); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java index ea0e279b..2e0055d9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java @@ -15,7 +15,6 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.ltrnet; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.dsp.fsk.LTRDecoderInstrumented; import io.github.dsheirer.sample.buffer.ReusableFloatBuffer; import javafx.beans.property.SimpleIntegerProperty; @@ -24,9 +23,9 @@ public class LTRNetDecoderInstrumented extends LTRNetDecoder { public SimpleIntegerProperty bufferCount = new SimpleIntegerProperty(); - public LTRNetDecoderInstrumented(DecodeConfigLTRNet config, AliasList aliasList) + public LTRNetDecoderInstrumented(DecodeConfigLTRNet config) { - super(config, aliasList, new LTRDecoderInstrumented(LTR_NET_MESSAGE_LENGTH)); + super(config, new LTRDecoderInstrumented(LTR_NET_MESSAGE_LENGTH)); } public LTRDecoderInstrumented getLTRDecoder() diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java index 7d8ce721..333e5c28 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java @@ -1,84 +1,94 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * 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.ltrnet; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedIntegerAttributeMonitor; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; +import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier; +import io.github.dsheirer.identifier.esn.ESNIdentifier; +import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.message.MessageDirection; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.util.StringUtils; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.ltrnet.channel.LtrNetChannel; +import io.github.dsheirer.module.decode.ltrnet.identifier.UniqueIdentifier; +import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswCallEnd; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswCallStart; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswUniqueId; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RegistrationRequestEsnHigh; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RegistrationRequestEsnLow; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RequestAccess; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ChannelMapHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ChannelMapLow; +import io.github.dsheirer.module.decode.ltrnet.message.osw.NeighborId; +import io.github.dsheirer.module.decode.ltrnet.message.osw.OswCallEnd; +import io.github.dsheirer.module.decode.ltrnet.message.osw.OswCallStart; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ReceiveFrequencyHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ReceiveFrequencyLow; +import io.github.dsheirer.module.decode.ltrnet.message.osw.RegistrationAccept; +import io.github.dsheirer.module.decode.ltrnet.message.osw.SiteId; +import io.github.dsheirer.module.decode.ltrnet.message.osw.SystemIdle; +import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow; +import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.TreeSet; public class LTRNetDecoderState extends DecoderState { private final static Logger mLog = LoggerFactory.getLogger(LTRNetDecoderState.class); - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); + private ChannelMapHigh mChannelMapHigh; + private ChannelMapLow mChannelMapLow; + private Map mChannelMap = new HashMap<>(); + private Map mCallDetectMap = new HashMap<>(); + private Map mNeighborMap = new HashMap<>(); + private SiteId mCurrentSite; + private int mCurrentChannelNumber; + private Set mTalkgroups = new TreeSet<>(); + private Set mUniqueIdentifiers = new TreeSet<>(); + private Set mESNIdentifiers = new TreeSet<>(); + private DecodeEvent mCurrentCallEvent; + private LTRTalkgroup mCurrentCallTalkgroup; - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mTalkgroupsFirstHeard = new TreeSet(); - private TreeSet mESNs = new TreeSet(); - private TreeSet mUniqueIDs = new TreeSet(); - private TreeSet mNeighborIDs = new TreeSet(); - private TreeSet mSiteIDs = new TreeSet(); - private HashMap mReceiveFrequencies = - new HashMap(); - private HashMap mTransmitFrequencies = - new HashMap(); - private HashMap mActiveCalls = new HashMap(); - - private AliasedStringAttributeMonitor mToAttribute; - private AliasedIntegerAttributeMonitor mFromUIDAttribute; - private AliasedStringAttributeMonitor mESNAttribute; - private String mMessage; - private String mMessageType; - private int mChannelNumber; - private long mFrequency = 0; - - public LTRNetDecoderState(AliasList aliasList) + public LTRNetDecoderState() { - super(aliasList); - - mToAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mToAttribute.addIllegalValue("0-00-000"); - - mFromUIDAttribute = new AliasedIntegerAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.LTR_NET_UID); - - mESNAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.ESN); } @Override @@ -88,235 +98,412 @@ public class LTRNetDecoderState extends DecoderState } @Override - public void receive(Message message) + protected IChannelDescriptor getCurrentChannel() { - if(message.isValid()) + if(mCurrentChannelNumber > 0) { - State state = State.IDLE; + return mChannelMap.get(mCurrentChannelNumber); + } - if(message instanceof LTRNetOSWMessage) + return null; + } + + /** + * Performs a full reset + */ + public void reset() + { + mChannelMapHigh = null; + mChannelMapLow = null; + mChannelMap.clear(); + mCallDetectMap.clear(); + mNeighborMap.clear(); + mCurrentSite = null; + mCurrentChannelNumber = 0; + mTalkgroups.clear(); + mUniqueIdentifiers.clear(); + mESNIdentifiers.clear(); + resetState(); + } + + private void processCallEnd(int channel, LTRTalkgroup talkgroup, long timestamp) + { + setCurrentChannelNumber(channel); + + if(mCurrentCallTalkgroup != null && mCurrentCallTalkgroup.equals(talkgroup)) + { + mCurrentCallEvent.end(timestamp); + broadcast(mCurrentCallEvent); + mCurrentCallEvent = null; + mCurrentCallTalkgroup = null; + getIdentifierCollection().remove(IdentifierClass.USER); + } + + if(talkgroup.getTalkgroup() == 254) + { + //Process CW Station ID broadcasts as an active state + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + } + else + { + broadcast(new DecoderStateEvent(this, Event.END, State.CALL)); + } + } + + private void processCallStart(int channel, LTRTalkgroup talkgroup, long timestamp, MessageDirection direction) + { + if(direction == MessageDirection.ISW) + { + setCurrentChannelNumber(channel); + } + + if(channel == mCurrentChannelNumber || mCurrentChannelNumber == 0) + { + setCurrentChannelNumber(channel); + + if(mCurrentCallTalkgroup == null || !mCurrentCallTalkgroup.equals(talkgroup)) { - LTRNetOSWMessage ltr = (LTRNetOSWMessage) message; + getIdentifierCollection().remove(IdentifierClass.USER); + getIdentifierCollection().update(talkgroup); + mCurrentCallEvent = DecodeEvent.builder(timestamp) + .protocol(Protocol.LTR_NET) + .channel(getCurrentChannel()) + .identifiers(getIdentifierCollection().copyOf()) + .build(); + mCurrentCallTalkgroup = talkgroup; + } - switch(ltr.getMessageType()) + //This updates the timestamp + mCurrentCallEvent.update(timestamp); + + if(talkgroup.getTalkgroup() == 253) + { + mCurrentCallEvent.setEventDescription("Register"); + broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); + } + else if(talkgroup.getTalkgroup() == 254) + { + mCurrentCallEvent.setEventDescription("FCC CWID"); + broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); + } + else + { + mCurrentCallEvent.setEventDescription("Call"); + broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); + } + + broadcast(mCurrentCallEvent); + } + else + { + DecodeEvent decodeEvent = mCallDetectMap.get(channel); + + if(decodeEvent == null) + { + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(talkgroup); + + decodeEvent = DecodeEvent.builder(timestamp) + .eventDescription("Call Detect") + .channel(mChannelMap.get(channel)) + .protocol(Protocol.LTR_NET) + .identifiers(ic) + .build(); + mCallDetectMap.put(channel, decodeEvent); + } + else + { + Identifier eventTalkgroup = decodeEvent.getIdentifierCollection() + .getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO); + + if(eventTalkgroup == null || !eventTalkgroup.equals(talkgroup) || + (timestamp - decodeEvent.getTimeStart() - decodeEvent.getDuration() > 2000)) { - case CA_ENDD: - if(mChannelNumber == 0) - { - setChannelNumber(ltr.getChannel()); - } + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(talkgroup); - /* Process FCC Station ID Events */ - if(ltr.getGroup() == 254) - { - if(mCurrentCallEvent == null || - mCurrentCallEvent.getCallEventType() != - CallEvent.CallEventType.STATION_ID) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEvent.CallEventType.STATION_ID) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(mFrequency) - .to(ltr.getTalkgroupID()) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); - } - else - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); - } - } - else - { - processCallEndMessage(ltr); - } - break; - case CA_STRT: - if(mChannelNumber == 0) - { - setChannelNumber(ltr.getChannel()); - } - - /* If the call event channel matches our current channel - * then it's a call, otherwise it's a call detect. */ - if(ltr.getChannel() == mChannelNumber) - { - processCallMessage(ltr); - } - else - { - processCallDetectMessage(ltr); - } - - break; - case SY_IDLE: - if(mChannelNumber != ltr.getChannel()) - { - setChannelNumber(ltr.getChannel()); - } - break; - case MA_CHNH: - break; - case MA_CHNL: - break; - case FQ_RXHI: - case FQ_RXLO: - if(ltr.getFrequency() > 0) - { - mReceiveFrequencies.put(ltr.getHomeRepeater(), ltr.getFrequency()); - } - break; - case FQ_TXHI: - case FQ_TXLO: - if(ltr.getFrequency() > 0) - { - mTransmitFrequencies.put(ltr.getHomeRepeater(), ltr.getFrequency()); - } - break; - case ID_NBOR: - String neighborID = ltr.getNeighborID(); - - if(neighborID != null) - { - mNeighborIDs.add(neighborID); - } - break; - case ID_UNIQ: - state = State.DATA; - - int uniqueID = ltr.getRadioUniqueID(); - - if(uniqueID != LTRNetOSWMessage.INT_NULL_VALUE) - { - mUniqueIDs.add(uniqueID); - } - - if(getCurrentLTRCallEvent() == null) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(mFrequency) - .from(String.valueOf(uniqueID)) - .build(); - } - else - { - mCurrentCallEvent.setFromID( - String.valueOf(ltr.getRadioUniqueID())); - mCurrentCallEvent.setDetails("Unique ID"); - - broadcast(mCurrentCallEvent); - } - - mFromUIDAttribute.process(uniqueID); - break; - case ID_SITE: - String siteID = ltr.getSiteID(); - - if(siteID != null) - { - mSiteIDs.add(siteID); - } - break; - default: - break; + decodeEvent = DecodeEvent.builder(timestamp) + .eventDescription("Call Detect") + .channel(mChannelMap.get(channel)) + .protocol(Protocol.LTR_NET) + .identifiers(ic) + .build(); + mCallDetectMap.put(channel, decodeEvent); } } - else if(message instanceof LTRNetISWMessage) + + decodeEvent.update(timestamp); + broadcast(decodeEvent); + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + } + } + + /** + * Updates the current call event whenever any identifiers related to the call change + */ + private void updateCurrentCallIdentifiers() + { + if(mCurrentCallEvent != null) + { + mCurrentCallEvent.setIdentifierCollection(getIdentifierCollection().copyOf()); + broadcast(mCurrentCallEvent); + } + } + + /** + * Updates the uplink or receive frequency for the repeater identified by the Logical Channel Number (LCN). + */ + private void updateReceiveFrequency(int channel, long frequency) + { + if(0 < channel && channel <= 20 && frequency > 0) + { + LtrNetChannel ltrNetChannel = mChannelMap.get(channel); + + if(ltrNetChannel == null) { - LTRNetISWMessage ltr = ((LTRNetISWMessage) message); - - switch(ltr.getMessageType()) - { - case CA_STRT: - processCallMessage(ltr); - break; - case CA_ENDD: - processCallEndMessage(ltr); - break; - case ID_ESNH: - case ID_ESNL: - state = State.DATA; - - String esn = ltr.getESN(); - - if(!esn.contains("xxxx")) - { - mESNs.add(ltr.getESN()); - } - - setMessageType("ESN"); - mESNAttribute.process(ltr.getESN()); - - broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEvent.CallEventType.REGISTER_ESN) - .aliasList(getAliasList()) - .details("ESN:" + ltr.getESN()) - .frequency(mFrequency) - .from(ltr.getESN()) - .build(); - - broadcast(mCurrentCallEvent); - } - - break; - case ID_UNIQ: - state = State.DATA; - - int uniqueid = ltr.getRadioUniqueID(); - - if(uniqueid != LTRNetISWMessage.INT_NULL_VALUE) - { - mUniqueIDs.add(uniqueid); - - setMessageType("REGISTER UID"); - mFromUIDAttribute.process(uniqueid); - - if(getCurrentLTRCallEvent() == null) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(mFrequency) - .from(String.valueOf(uniqueid)) - .build(); - } - else - { - mCurrentCallEvent.setFromID( - String.valueOf(ltr.getRadioUniqueID())); - mCurrentCallEvent.setDetails("Unique ID"); - - broadcast(mCurrentCallEvent); - } - } - break; - default: - break; - } + ltrNetChannel = new LtrNetChannel(channel); + ltrNetChannel.setUplink(frequency); + mChannelMap.put(channel, ltrNetChannel); + } + else + { + ltrNetChannel.setUplink(frequency); } } } - public LTRCallEvent getCurrentLTRCallEvent() + /** + * Updates the downlink or transmit frequency for the repeater identified by the Logical Channel Number (LCN). + */ + private void updateTransmitFrequency(int channel, long frequency) { - if(mCurrentCallEvent != null) + if(0 < channel && channel <= 20 && frequency > 0) { - return (LTRCallEvent) mCurrentCallEvent; - } + LtrNetChannel ltrNetChannel = mChannelMap.get(channel); - return null; + if(ltrNetChannel == null) + { + ltrNetChannel = new LtrNetChannel(channel); + ltrNetChannel.setDownlink(frequency); + mChannelMap.put(channel, ltrNetChannel); + } + else + { + ltrNetChannel.setDownlink(frequency); + } + } + } + + @Override + public void receive(IMessage message) + { + if(message.isValid() && message instanceof LtrNetMessage) + { + switch(((LtrNetMessage)message).getLtrNetMessageType()) + { + case ISW_CALL_END: + if(message instanceof IswCallEnd) + { + IswCallEnd callEnd = (IswCallEnd)message; + mTalkgroups.add(callEnd.getTalkgroup()); + processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp()); + } + break; + case ISW_CALL_START: + if(message instanceof IswCallStart) + { + IswCallStart callStart = (IswCallStart)message; + mTalkgroups.add(callStart.getTalkgroup()); + processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(), + MessageDirection.ISW); + } + break; + case ISW_REGISTRATION_REQUEST_ESN_HIGH: + if(message instanceof RegistrationRequestEsnHigh) + { + RegistrationRequestEsnHigh registrationRequestEsn = (RegistrationRequestEsnHigh)message; + + if(registrationRequestEsn.isCompleteEsn()) + { + getIdentifierCollection().update(registrationRequestEsn.getESN()); + mESNIdentifiers.add(registrationRequestEsn.getESN()); + } + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .eventDescription("Registration Request") + .details(registrationRequestEsn.toString()) + .identifiers(getIdentifierCollection().copyOf()) + .channel(getCurrentChannel()) + .protocol(Protocol.LTR_NET) + .build()); + + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); + } + break; + case ISW_REGISTRATION_REQUEST_ESN_LOW: + if(message instanceof RegistrationRequestEsnLow) + { + RegistrationRequestEsnLow registrationRequestEsn = (RegistrationRequestEsnLow)message; + + if(registrationRequestEsn.isCompleteEsn()) + { + getIdentifierCollection().update(registrationRequestEsn.getESN()); + mESNIdentifiers.add(registrationRequestEsn.getESN()); + } + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .eventDescription("Registration Request") + .details(registrationRequestEsn.toString()) + .identifiers(getIdentifierCollection().copyOf()) + .channel(getCurrentChannel()) + .protocol(Protocol.LTR_NET) + .build()); + + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); + } + break; + case ISW_REQUEST_ACCESS: + if(message instanceof RequestAccess) + { + RequestAccess requestAccess = (RequestAccess)message; + + getIdentifierCollection().update(requestAccess.getTalkgroup()); + mTalkgroups.add(requestAccess.getTalkgroup()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .eventDescription("Access Request") + .details(requestAccess.toString()) + .identifiers(getIdentifierCollection().copyOf()) + .channel(getCurrentChannel()) + .protocol(Protocol.LTR_NET) + .build()); + + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); + } + break; + case ISW_UNIQUE_ID: + if(message instanceof IswUniqueId) + { + IswUniqueId iswUniqueId = (IswUniqueId)message; + getIdentifierCollection().update(iswUniqueId.getUniqueID()); + mUniqueIdentifiers.add(iswUniqueId.getUniqueID()); + updateCurrentCallIdentifiers(); + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); + } + break; + case ISW_UNKNOWN: + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_CALL_END: + if(message instanceof OswCallEnd) + { + OswCallEnd callEnd = (OswCallEnd)message; + mTalkgroups.add(callEnd.getTalkgroup()); + processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp()); + } + break; + case OSW_CALL_START: + if(message instanceof OswCallStart) + { + OswCallStart callStart = (OswCallStart)message; + mTalkgroups.add(callStart.getTalkgroup()); + processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(), + MessageDirection.OSW); + } + break; + case OSW_CHANNEL_MAP_HIGH: + if(message instanceof ChannelMapHigh) + { + mChannelMapHigh = (ChannelMapHigh)message; + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_CHANNEL_MAP_LOW: + if(message instanceof ChannelMapLow) + { + mChannelMapLow = (ChannelMapLow)message; + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_SYSTEM_IDLE: + if(message instanceof SystemIdle) + { + setCurrentChannelNumber(((SystemIdle)message).getChannel()); + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_NEIGHBOR_ID: + if(message instanceof NeighborId) + { + NeighborId neighborId = (NeighborId)message; + mNeighborMap.put(neighborId.getNeighborRank(), neighborId); + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_RECEIVE_FREQUENCY_HIGH: + if(message instanceof ReceiveFrequencyHigh) + { + ReceiveFrequencyHigh receiveFrequency = (ReceiveFrequencyHigh)message; + updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency()); + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_RECEIVE_FREQUENCY_LOW: + if(message instanceof ReceiveFrequencyLow) + { + ReceiveFrequencyLow receiveFrequency = (ReceiveFrequencyLow)message; + updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency()); + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_REGISTRATION_ACCEPT: + if(message instanceof RegistrationAccept) + { + RegistrationAccept registrationAccept = (RegistrationAccept)message; + mUniqueIdentifiers.add(registrationAccept.getUniqueID()); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(message.getIdentifiers()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.LTR_NET) + .channel(getCurrentChannel()) + .identifiers(ic) + .eventDescription("Registration Accept") + .details(registrationAccept.toString()) + .build()); + } + break; + case OSW_SITE_ID: + if(message instanceof SiteId) + { + mCurrentSite = (SiteId)message; + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_TRANSMIT_FREQUENCY_HIGH: + if(message instanceof TransmitFrequencyHigh) + { + TransmitFrequencyHigh transmitFrequency = (TransmitFrequencyHigh)message; + updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency()); + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_TRANSMIT_FREQUENCY_LOW: + if(message instanceof TransmitFrequencyLow) + { + TransmitFrequencyLow transmitFrequency = (TransmitFrequencyLow)message; + updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency()); + } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + case OSW_UNKNOWN: + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); + break; + } + } } @Override @@ -326,102 +513,55 @@ public class LTRNetDecoderState extends DecoderState sb.append("Activity Summary\n"); sb.append("Decoder:\tLTR-Net\n\n"); + sb.append("Site:\t").append(mCurrentSite != null ? mCurrentSite.getSiteID() : "Unknown").append("\n"); - if(mSiteIDs.isEmpty()) + sb.append("\nLCNs (transmit / receive)\n"); + + if(mChannelMapLow != null) { - sb.append("Site:\tUnknown\n"); + for(int channel: mChannelMapLow.getChannels()) + { + LtrNetChannel ltrNetChannel = mChannelMap.get(channel); + sb.append(" ").append(channel).append(": ").append(ltrNetChannel != null ? ltrNetChannel.description() : + "Unknown").append(mCurrentChannelNumber == channel ? " (Current)\n" : "\n"); + } } else { - Iterator it = mSiteIDs.iterator(); - - while(it.hasNext()) - { - sb.append("Site:\t"); - - String siteID = it.next(); - - sb.append(siteID); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(String.valueOf(siteID)); - - if(siteAlias != null) - { - sb.append(" "); - sb.append(siteAlias.getName()); - } - } - - sb.append("\n"); - } + sb.append("Channel Map 1-10: Unknown\n"); } - sb.append("\nLCNs (transmit | receive)\n"); + if(mChannelMapHigh != null) + { + for(int channel: mChannelMapHigh.getChannels()) + { + LtrNetChannel ltrNetChannel = mChannelMap.get(channel); + sb.append(" ").append(channel).append(": ").append(ltrNetChannel != null ? ltrNetChannel.description() : + "Unknown").append(mCurrentChannelNumber == channel ? " (Current)\n" : "\n"); + } + } + else + { + sb.append("Channel Map 11-20: Unknown\n"); + } - if(mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty()) + sb.append("\nNeighbor Sites (Rank: ID)\n"); + + if(mNeighborMap.isEmpty()) { sb.append(" None\n"); } else { - for(int x = 1; x < 21; x++) + List ranks = new ArrayList<>(mNeighborMap.keySet()); + Collections.sort(ranks); + for(Integer rank: ranks) { - long rcv = 0; - - if(mReceiveFrequencies.containsKey(x)) - { - rcv = mReceiveFrequencies.get(x); - } - - long xmt = 0; - - if(mTransmitFrequencies.containsKey(x)) - { - xmt = mTransmitFrequencies.get(x); - } - - if(rcv > 0 || xmt > 0) - { - if(x < 10) - { - sb.append(" "); - } - - sb.append(x); - sb.append(": "); - - if(xmt == 0) - { - sb.append("---.-----"); - } - else - { - sb.append(mDecimalFormatter.format((double) xmt / 1E6d)); - } - sb.append(" | "); - - if(rcv == 0) - { - sb.append("---.-----"); - } - else - { - sb.append(mDecimalFormatter.format((double) rcv / 1E6d)); - } - - if(x == mChannelNumber) - { - sb.append(" **"); - } - - sb.append("\n"); - } + sb.append(" ").append(rank).append(": ").append(mNeighborMap.get(rank).getNeighborID()).append("\n"); } } - sb.append("\nTalkgroups\n"); + sb.append("\nActive Talkgroups\n"); if(mTalkgroups.isEmpty()) { @@ -429,355 +569,90 @@ public class LTRNetDecoderState extends DecoderState } else { - Iterator it = mTalkgroups.iterator(); + List talkgroups = new ArrayList<>(mTalkgroups); + Collections.sort(talkgroups, Comparator.comparingInt(Identifier::getValue)); - while(it.hasNext()) + for(LTRTalkgroup talkgroup: talkgroups) { - String tgid = it.next(); - - sb.append(" "); - sb.append(tgid); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(tgid); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); + sb.append(" ").append(talkgroup.formatted()).append("\n"); } } - sb.append("\nRadio Unique IDs\n"); + sb.append("\nActive Radio Unique IDs\n"); - if(mUniqueIDs.isEmpty()) + if(mUniqueIdentifiers.isEmpty()) { sb.append(" None\n"); } else { - Iterator it = mUniqueIDs.iterator(); + List uniqueIdentifiers = new ArrayList<>(mUniqueIdentifiers); + Collections.sort(uniqueIdentifiers, Comparator.comparingInt(Identifier::getValue)); - while(it.hasNext()) + for(UniqueIdentifier uniqueIdentifier: uniqueIdentifiers) { - int uid = it.next(); - - sb.append(" "); - sb.append(uid); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getUniqueID(uid); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); + sb.append(" ").append(uniqueIdentifier).append("\n"); } } - sb.append("\nESNs\n"); + sb.append("\nActive ESNs\n"); - if(mESNs.isEmpty()) + if(mESNIdentifiers.isEmpty()) { sb.append(" None\n"); } else { - Iterator it = mESNs.iterator(); + List esnIdentifiers = new ArrayList<>(mESNIdentifiers); + Collections.sort(esnIdentifiers); - while(it.hasNext()) + for(ESNIdentifier esnIdentifier: esnIdentifiers) { - String esn = it.next(); - - sb.append(" "); - sb.append(esn); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getESNAlias(esn); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nNeighbor Sites\n"); - - if(mNeighborIDs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mNeighborIDs.iterator(); - - while(it.hasNext()) - { - String neighbor = it.next(); - - sb.append(" "); - sb.append(neighbor); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getSiteID(String.valueOf(neighbor)); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); + sb.append(" ").append(esnIdentifier).append("\n"); } } return sb.toString(); } - /** - * Call Detect - current channel messages indicate a call on another channel - */ - private void processCallDetectMessage(LTRNetMessage message) - { - if(!mActiveCalls.containsKey(message.getChannel()) || - !mActiveCalls.get(message.getChannel()).contentEquals(message.getTalkgroupID())) - { - mActiveCalls.put(message.getChannel(), message.getTalkgroupID()); - - int channel = message.getChannel(); - - long frequency = 0; - - if(mTransmitFrequencies.containsKey(channel)) - { - frequency = mTransmitFrequencies.get(channel); - } - - broadcast(new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(String.valueOf(message.getChannel())) - .frequency(frequency) - .to(message.getTalkgroupID()) - .build()); - } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); - } - - /** - * Indicates if the talkgroup is different than the talkgroup specified in - * the current call event - */ - private boolean isDifferentTalkgroup(String talkgroup) - { - return talkgroup != null && - mCurrentCallEvent != null && - mCurrentCallEvent.getToID() != null && - !mCurrentCallEvent.getToID().contentEquals(talkgroup); - } - - private boolean isValidTalkgroup(String talkgroup) - { - return talkgroup != null && !talkgroup.matches("0-00-000"); - } - - private void processCallMessage(LTRNetMessage message) - { - int group = message.getGroup(); - - /* Process call registration */ - if(group == 253) - { - setMessageType("REGISTER"); - broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); - } - /* Process call */ - else - { - String talkgroup = message.getTalkgroupID(); - - if(isValidTalkgroup(talkgroup)) - { - mToAttribute.process(talkgroup); - - final LTRCallEvent current = getCurrentLTRCallEvent(); - - //If this is a new call or the talkgroup is different from the current call, create a new call event - if(current == null || isDifferentTalkgroup(talkgroup)) - { - /* Invalidate the current call */ - if(current != null) - { - current.setValid(false); - broadcast(current); - } - - /* A talkgroup must be seen at least once before it will be added - * to the mTalkgroups list that is used in the activity summary, - * so that we don't pollute the summary with one-off error talkgroups */ - if(mTalkgroupsFirstHeard.contains(talkgroup)) - { - mTalkgroups.add(talkgroup); - } - else - { - mTalkgroupsFirstHeard.add(talkgroup); - } - - CallEvent callEvent = new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEvent.CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(message.getChannel())) - .frequency(mFrequency) - .to(message.getTalkgroupID()) - .build(); - - broadcast(callEvent); - - mCurrentCallEvent = callEvent; - - broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); - } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); - } - } - } - - private void processCallEndMessage(LTRNetMessage message) - { - if(mCurrentCallEvent != null && - mCurrentCallEvent.getCallEventType() == CallEvent.CallEventType.CALL) - { - mCurrentCallEvent.setEnd(System.currentTimeMillis()); - broadcast(mCurrentCallEvent); - } - - mCurrentCallEvent = null; - - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - } - - /** - * Performs a full reset - */ - public void reset() - { - mChannelNumber = 0; - - mActiveCalls.clear(); - mESNs.clear(); - mNeighborIDs.clear(); - mReceiveFrequencies.clear(); - mSiteIDs.clear(); - mTalkgroups.clear(); - mTransmitFrequencies.clear(); - mUniqueIDs.clear(); - - resetState(); - } - @Override public void start() { - } @Override public void stop() { - } @Override public void init() { - } /** * Resets the decoder state after a call or other decode event */ - private void resetState() + protected void resetState() { - if(mCurrentCallEvent != null && mCurrentCallEvent - .getCallEventType() == CallEvent.CallEventType.CALL) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - } - + super.resetState(); mCurrentCallEvent = null; - - mToAttribute.reset(); - mESNAttribute.reset(); - mFromUIDAttribute.reset(); - mMessage = null; - mMessageType = null; + mCurrentCallTalkgroup = null; } - public String getMessage() + public void setCurrentChannelNumber(int channelNumber) { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) + if(0 < channelNumber && channelNumber <= 20) { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, mMessageType)); - } - } + mCurrentChannelNumber = channelNumber; - public String getMessageType() - { - return mMessageType; - } + LtrNetChannel currentChannel = mChannelMap.get(mCurrentChannelNumber); - private void setMessageType(String messageType) - { - if(!StringUtils.isEqual(mMessageType, messageType)) - { - mMessageType = messageType; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, mMessageType)); - } - } - - public boolean hasChannelNumber() - { - return mChannelNumber != 0; - } - - public int getChannelNumber() - { - return mChannelNumber; - } - - public void setChannelNumber(int channelNumber) - { - if(mChannelNumber != channelNumber) - { - mChannelNumber = channelNumber; - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, - "LCN:" + getChannelNumber())); + if(currentChannel != null) + { + getIdentifierCollection().update(DecoderLogicalChannelNameIdentifier + .create(String.valueOf(mCurrentChannelNumber), Protocol.LTR_NET)); + } } } @@ -789,9 +664,6 @@ public class LTRNetDecoderState extends DecoderState case RESET: resetState(); break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - break; default: break; } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetISWMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetISWMessage.java deleted file mode 100644 index 0f0b6c19..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetISWMessage.java +++ /dev/null @@ -1,345 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.ltrnet; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.edac.CRCLTR; -import io.github.dsheirer.map.Plottable; -import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class LTRNetISWMessage extends LTRNetMessage -{ - private static final int sCHIU_UNIQUE_ID = 24; - private static final int sCHIU_ESN_LOW = 27; - private static final int sCHIU_ESN_HIGH = 29; - - private static final int sFREE_CALL = 21; - private static final int sFREE_END_CALL = 23; - private static final int sFREE_REQUEST_ACCESS = 31; - - - private LTRNetISWMessage mAuxMessage; - - public LTRNetISWMessage( BinaryMessage message, AliasList list ) - { - super( message, MessageDirection.ISW, list ); - - /** - * If the CRC fails, test for a transmitted CRC of 127 and then check - * the Free field for special messages - */ - if( mCRC == CRC.FAILED_CRC && CRCLTR.getTransmittedChecksum( mMessage ) == 127 ) - { - if( getFree() == sFREE_REQUEST_ACCESS || - getFree() == sFREE_END_CALL ) - { - mCRC = CRC.PASSED; - } - } - - mMessageType = getMessageType(); - } - - public void setAuxiliaryMessage( LTRNetISWMessage message ) - { - if( message != null ) - { - mAuxMessage = message; - } - } - - public String getESN() - { - if( mMessageType == MessageType.ID_ESNH ) - { - if( mAuxMessage != null && - mAuxMessage.getMessageType() == MessageType.ID_ESNL ) - { - return getESNHigh() + mAuxMessage.getESNLow(); - } - else - { - return getESNHigh() + "xxxx"; - } - } - else - { - if( mAuxMessage != null && - mAuxMessage.getMessageType() == MessageType.ID_ESNH ) - { - return mAuxMessage.getESNHigh() + getESNLow(); - } - else - { - return "xxxx" + getESNLow(); - } - } - } - - public Alias getESNAlias() - { - if( mAliasList != null ) - { - return mAliasList.getESNAlias( getESN() ); - } - - return null; - } - - public String getESNHigh() - { - int esnHigh = getInt( sSIXTEEN_BITS ); - - return String.format("%04X", esnHigh & 0xFFFF ); - } - - public String getESNLow() - { - int esnLow = getInt( sSIXTEEN_BITS ); - - return String.format("%04X", esnLow & 0xFFFF ); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append( mDatestampFormatter.format( new Date( System.currentTimeMillis() ) ) ); - sb.append( " LTRNet ISW [" ); - sb.append( mCRC.getAbbreviation() ); - sb.append( "] " ); - sb.append( getMessage() ); - pad( sb, 100 ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - switch( mMessageType ) - { - case CA_STRT: - sb.append( "CALL LCN:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " TG [ " ); - sb.append( getTalkgroupID() ); - sb.append( "/" ); - sb.append( getTalkgroupIDAlias() ); - sb.append( " ] FREE:" ); - sb.append( format( getFree(), 2 ) ); - break; - case CA_ENDD: - sb.append( "END* AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " TG [" ); - sb.append( getTalkgroupID() ); - sb.append( "/" ); - sb.append( getTalkgroupIDAlias() ); - sb.append( "] FREE:" ); - sb.append( format( getFree(), 2 ) ); - break; - case RQ_ACCE: - sb.append( "REQUEST ACCESS LCN:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " TG [ " ); - sb.append( getTalkgroupID() ); - sb.append( "/" ); - sb.append( getTalkgroupIDAlias() ); - sb.append( " ] FREE:" ); - sb.append( format( getFree(), 2 ) ); - break; - case ID_ESNL: - sb.append( "ESN LOW " ); - sb.append( getESN() ); - sb.append( "/" ); - sb.append( getESNAlias() ); - break; - case ID_ESNH: - sb.append( "ESN HIGH " ); - sb.append( getESN() ); - sb.append( "/" ); - sb.append( getESNAlias() ); - break; - case ID_UNIQ: - sb.append( "RADIO UNIQUE ID " ); - sb.append( getRadioUniqueID() ); - - if( getRadioUniqueIDAlias() != null ) - { - sb.append( "/" ); - sb.append( getRadioUniqueIDAlias().getName() ); - } - break; - default: - sb.append( "UNKNOWN " ); - sb.append( getArea() ); - sb.append( " " ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " " ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " " ); - sb.append( format( getGroup(), 3 ) ); - sb.append( " " ); - sb.append( format( getFree(), 2 ) ); - sb.append( " CRC:" ); - sb.append( CRCLTR.getTransmittedChecksum( mMessage ) ); - sb.append( " CALC:" ); - sb.append( CRCLTR.getCalculatedChecksum( mMessage ) ); - } - - return sb.toString(); - } - - public int getRadioUniqueID() - { - int retVal = INT_NULL_VALUE; - - if( mMessageType == MessageType.ID_UNIQ ) - { - retVal = getInt( sSIXTEEN_BITS ); - } - - return retVal; - } - - public Alias getRadioUniqueIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getUniqueID( getRadioUniqueID() ); - } - - return null; - } - - public MessageType getMessageType() - { - MessageType retVal = MessageType.UN_KNWN; - - int channel = getChannel(); - - if( channel == 31 ) - { - retVal = MessageType.CA_ENDD; - } - else if( channel > 20 ) - { - switch( channel ) - { - case sCHIU_UNIQUE_ID: - retVal = MessageType.ID_UNIQ; - break; - case sCHIU_ESN_LOW: - retVal = MessageType.ID_ESNH; - break; - case sCHIU_ESN_HIGH: - retVal = MessageType.ID_ESNL; - break; - } - } - else if( channel > 0 ) - { - int free = getFree(); - - switch( free ) - { - case sFREE_CALL: - retVal = MessageType.CA_STRT; - break; - case sFREE_END_CALL: - retVal = MessageType.CA_ENDD; - break; - case sFREE_REQUEST_ACCESS: - retVal = MessageType.RQ_ACCE; - break; - } - } - - return retVal; - } - @Override - public String getFromID() - { - return null; - } - - @Override - public Alias getFromIDAlias() - { - return null; - } - - @Override - public void setAuxiliaryMessage( LTRNetOSWMessage message ) - { - // TODO Auto-generated method stub - - } - - @Override - public Plottable getPlottable() - { - // TODO Auto-generated method stub - return null; - } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - Alias esn = getESNAlias(); - - if( esn != null ) - { - aliases.add( esn ); - } - - return aliases; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessage.java deleted file mode 100644 index 4973229f..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessage.java +++ /dev/null @@ -1,243 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.ltrnet; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.edac.CRCLTR; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; -import io.github.dsheirer.module.decode.DecoderType; - -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; - -public abstract class LTRNetMessage extends Message -{ - protected static final String sUNKNOWN = "**UNKNOWN**"; - protected static final int INT_NULL_VALUE = -1; - protected static final double sDOUBLE_NULL_VALUE = -1.0D; - protected SimpleDateFormat mDatestampFormatter = - new SimpleDateFormat( "yyyyMMdd HHmmss" ); - protected DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - protected static final int[] sSYNC = { 8,7,6,5,4,3,2,1,0 }; - protected static final int[] sAREA = { 9 }; - protected static final int[] sCHANNEL = { 14,13,12,11,10 }; - protected static final int[] sHOME_REPEATER = { 19,18,17,16,15 }; - protected static final int[] sGROUP = { 27,26,25,24,23,22,21,20 }; - protected static final int[] sFREE = { 32,31,30,29,28 }; - protected static final int[] sCRC = { 39,38,37,36,35,34,33 }; - protected static final int[] sSIXTEEN_BITS = { 32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17 }; - protected static final int[] sMANUFACTURER_ID = { 23,22,21,20,19,18,17 }; - - protected BinaryMessage mMessage; - protected MessageType mMessageType; - protected CRC mCRC; - protected AliasList mAliasList; - - public LTRNetMessage( BinaryMessage message, - MessageDirection direction, - AliasList list ) - { - mMessage = message; - mCRC = CRCLTR.check( message, direction ); - mAliasList = list; - } - - public MessageType getMessageType() - { - return mMessageType; - } - - public abstract void setAuxiliaryMessage( LTRNetOSWMessage message ); - - public boolean isValid() - { - return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY; - } - - /** - * Appends spaces to the end of the stringbuilder to make it length long - */ - protected void pad( StringBuilder sb, int length ) - { - while( sb.length() < length ) - { - sb.append( " " ); - } - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } - - /** - * Returns a value formatted in hex notation. - * - * @param value - integer value - * @return - hex digits returned - */ - protected static String getHex( int value, int digits ) - { - return String.format( "%0" + digits + "X", value ); - } - - protected int getInt( int[] bits ) - { - int retVal = 0; - - for( int x = 0; x < bits.length; x++ ) - { - if( mMessage.get( bits[ x ] ) ) - { - retVal += 1< +public class LTRNetMessageFilter extends Filter { - private HashMap> mElements = - new HashMap>(); - - public LTRNetMessageFilter() - { - super( "LTR-Net Message Filter" ); + private Map> mElements = new HashMap<>(); - mElements.put( MessageType.CA_STRT, - new FilterElement( MessageType.CA_STRT ) ); - mElements.put( MessageType.CA_ENDD, - new FilterElement( MessageType.CA_ENDD ) ); - mElements.put( MessageType.SY_IDLE, - new FilterElement( MessageType.SY_IDLE ) ); - mElements.put( MessageType.ID_UNIQ, - new FilterElement( MessageType.ID_UNIQ ) ); - mElements.put( MessageType.ID_ESNH, - new FilterElement( MessageType.ID_ESNH ) ); - mElements.put( MessageType.ID_ESNL, - new FilterElement( MessageType.ID_ESNL ) ); - mElements.put( MessageType.RQ_ACCE, - new FilterElement( MessageType.RQ_ACCE ) ); - mElements.put( MessageType.ID_SITE, - new FilterElement( MessageType.ID_SITE ) ); - mElements.put( MessageType.FQ_RXHI, - new FilterElement( MessageType.FQ_RXHI ) ); - mElements.put( MessageType.FQ_RXLO, - new FilterElement( MessageType.FQ_RXLO ) ); - mElements.put( MessageType.FQ_TXHI, - new FilterElement( MessageType.FQ_TXHI ) ); - mElements.put( MessageType.FQ_TXLO, - new FilterElement( MessageType.FQ_TXLO ) ); - mElements.put( MessageType.ID_NBOR, - new FilterElement( MessageType.ID_NBOR ) ); - mElements.put( MessageType.MA_CHNH, - new FilterElement( MessageType.MA_CHNH ) ); - mElements.put( MessageType.MA_CHNL, - new FilterElement( MessageType.MA_CHNL ) ); - } - - @Override - public boolean passes( Message message ) + public LTRNetMessageFilter() { - if( mEnabled && canProcess( message ) ) - { - LTRNetMessage ltr = (LTRNetMessage)message; - - if( mElements.containsKey( ltr.getMessageType() ) ) - { - return mElements.get( ltr.getMessageType() ).isEnabled(); - } - } - - return false; + super("LTR-Net Message Filter"); + + for(LtrNetMessageType type: LtrNetMessageType.values()) + { + mElements.put(type, new FilterElement<>(type)); + } } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof LTRNetMessage; + if(mEnabled && canProcess(message)) + { + LtrNetMessage ltr = (LtrNetMessage) message; + + if(mElements.containsKey(ltr.getLtrNetMessageType())) + { + return mElements.get(ltr.getLtrNetMessageType()).isEnabled(); + } + } + + return false; } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof LtrNetMessage; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mElements.values() ); + return new ArrayList>(mElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageProcessor.java index b40268cf..4dab1a1e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageProcessor.java @@ -1,164 +1,132 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014,2015 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.ltrnet; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; +import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage; +import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessageFactory; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RegistrationRequestEsnHigh; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RegistrationRequestEsnLow; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ReceiveFrequencyHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ReceiveFrequencyLow; +import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow; import io.github.dsheirer.sample.Listener; import java.util.HashMap; +import java.util.Map; -public class LTRNetMessageProcessor implements Listener +public class LTRNetMessageProcessor implements Listener { - private Listener mMessageListener; + private Listener mMessageListener; - private HashMap mReceiveHighAuxMessages = - new HashMap(); - private HashMap mReceiveLowAuxMessages = - new HashMap(); + private Map mReceiveHighMessageMap = new HashMap<>(); + private Map mReceiveLowMessageMap = new HashMap<>(); + private Map mTransmitHighMessageMap = new HashMap<>(); + private Map mTransmitLowMessageMap = new HashMap<>(); - private HashMap mTransmitHighAuxMessages = - new HashMap(); - private HashMap mTransmitLowAuxMessages = - new HashMap(); + private RegistrationRequestEsnHigh mRegistrationRequestEsnHighMessage; + private RegistrationRequestEsnLow mRegistrationRequestEsnLowMessage; - private LTRNetISWMessage mESNHighMessage; - private LTRNetISWMessage mESNLowMessage; - - private MessageDirection mDirection; - private AliasList mAliasList; - - public LTRNetMessageProcessor( MessageDirection direction, AliasList list ) - { - mDirection = direction; - mAliasList = list; - } - - @Override - public void receive( BinaryMessage buffer ) + private MessageDirection mDirection; + + public LTRNetMessageProcessor(MessageDirection direction) { - if( mMessageListener != null ) - { - LTRNetMessage message; - - if( mDirection == MessageDirection.OSW ) - { - message = new LTRNetOSWMessage( buffer, mAliasList ); - - if( message.isValid() ) - { - if( message.getMessageType() == MessageType.FQ_TXHI ) - { - mTransmitHighAuxMessages.put( message.getHomeRepeater(), (LTRNetOSWMessage)message ); - - message.setAuxiliaryMessage( mTransmitLowAuxMessages.get(message.getHomeRepeater() ) ); - } - else if( message.getMessageType() == MessageType.FQ_TXLO ) - { - mTransmitLowAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - - message.setAuxiliaryMessage( mTransmitHighAuxMessages.get( - message.getHomeRepeater() ) ); - } - else if( message.getMessageType() == MessageType.FQ_RXHI ) - { - mReceiveHighAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - - message.setAuxiliaryMessage( mReceiveLowAuxMessages.get( - message.getHomeRepeater() ) ); - } - else if( message.getMessageType() == MessageType.FQ_RXLO ) - { - mReceiveLowAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - message.setAuxiliaryMessage( mReceiveHighAuxMessages.get( - message.getHomeRepeater() ) ); - } - } - } - else - { - buffer.flip( 0, 40 ); - - message = new LTRNetISWMessage( buffer, mAliasList ); - - if( message.isValid() ) - { - /** - * Catch ESN High messages to marry up with ESN Low messages. Purge - * the ESN high message after 2 messages, so that it doesn't get - * paired with the wrong esn low message - */ - if( message.getMessageType() == MessageType.ID_ESNH ) - { - mESNHighMessage = (LTRNetISWMessage)message; - - if( mESNLowMessage != null ) - { - /* Check that the stored message is not older than - * 5 seconds */ - if( System.currentTimeMillis() - - mESNLowMessage.getTimeReceived() < 5000 ) - { - ((LTRNetISWMessage)message).setAuxiliaryMessage( mESNLowMessage ); - } - else - { - mESNLowMessage = null; - } - } - } - else if( message.getMessageType() == MessageType.ID_ESNL ) - { - mESNLowMessage = (LTRNetISWMessage)message; - - if( mESNHighMessage != null ) - { - /* Check that the stored message is not older than 5 seconds */ - if( System.currentTimeMillis() - mESNHighMessage.getTimeReceived() < 5000 ) - { - ((LTRNetISWMessage)message).setAuxiliaryMessage( mESNHighMessage ); - } - else - { - mESNHighMessage = null; - } - } - } - } - } - - mMessageListener.receive( message ); - } + mDirection = direction; } - - public void setMessageListener( Listener listener ) + + @Override + public void receive(CorrectedBinaryMessage buffer) { - mMessageListener = listener; + LtrNetMessage message = LtrNetMessageFactory.create(mDirection, buffer, System.currentTimeMillis()); + + if(message.isValid()) + { + switch(message.getLtrNetMessageType()) + { + case OSW_TRANSMIT_FREQUENCY_HIGH: + if(message instanceof TransmitFrequencyHigh) + { + TransmitFrequencyHigh transmitFrequencyHigh = (TransmitFrequencyHigh)message; + mTransmitHighMessageMap.put(transmitFrequencyHigh.getChannel(), transmitFrequencyHigh); + transmitFrequencyHigh.setFrequencyLow(mTransmitLowMessageMap.get(transmitFrequencyHigh.getChannel())); + } + break; + case OSW_TRANSMIT_FREQUENCY_LOW: + if(message instanceof TransmitFrequencyLow) + { + TransmitFrequencyLow transmitFrequencyLow = (TransmitFrequencyLow)message; + mTransmitLowMessageMap.put(transmitFrequencyLow.getChannel(), transmitFrequencyLow); + transmitFrequencyLow.setFrequencyHigh(mTransmitHighMessageMap.get(transmitFrequencyLow.getChannel())); + } + break; + case OSW_RECEIVE_FREQUENCY_HIGH: + if(message instanceof ReceiveFrequencyHigh) + { + ReceiveFrequencyHigh receiveFrequencyHigh = (ReceiveFrequencyHigh)message; + mReceiveHighMessageMap.put(receiveFrequencyHigh.getChannel(), receiveFrequencyHigh); + receiveFrequencyHigh.setFrequencyLow(mReceiveLowMessageMap.get(receiveFrequencyHigh.getChannel())); + } + break; + case OSW_RECEIVE_FREQUENCY_LOW: + if(message instanceof ReceiveFrequencyLow) + { + ReceiveFrequencyLow receiveFrequencyLow = (ReceiveFrequencyLow)message; + mReceiveLowMessageMap.put(receiveFrequencyLow.getChannel(), receiveFrequencyLow); + receiveFrequencyLow.setFrequencyHigh(mReceiveHighMessageMap.get(receiveFrequencyLow.getChannel())); + } + break; + case ISW_REGISTRATION_REQUEST_ESN_HIGH: + if(message instanceof RegistrationRequestEsnHigh) + { + ((RegistrationRequestEsnHigh)message).setEsnLowMessage(mRegistrationRequestEsnLowMessage); + } + break; + case ISW_REGISTRATION_REQUEST_ESN_LOW: + if(message instanceof RegistrationRequestEsnLow) + { + ((RegistrationRequestEsnLow)message).setESNHighMessage(mRegistrationRequestEsnHighMessage); + } + break; + case ISW_CALL_START: + case ISW_CALL_END: + //Reset the esn messages + mRegistrationRequestEsnHighMessage = null; + mRegistrationRequestEsnLowMessage = null; + break; + } + + if(mMessageListener != null) + { + mMessageListener.receive(message); + } + } + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetOSWMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetOSWMessage.java deleted file mode 100644 index 89368985..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetOSWMessage.java +++ /dev/null @@ -1,644 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.ltrnet; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.map.Plottable; -import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class LTRNetOSWMessage extends LTRNetMessage -{ - private static final int CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20; - private static final int CHANNEL_MAP_MESSAGE_TYPE_BIT = 17; - - private static final int CHIU_UNIQUE_ID = 17; - private static final int CHIU_SITE_ID = 18; - private static final int CHIU_TRANSMIT_FREQUENCY = 24; - private static final int CHIU_RECEIVE_FREQUENCY = 25; - private static final int CHIU_NEIGHBOR = 26; - private static final int CHIU_MAP = 28; - private static final int CHIU_CALL_END = 31; - - private static final int HOME_DIRECTED_GROUP_CALL = 29; - private static final int HOME_CHANNEL_IN_USE_MUTE_AUDIO = 30; - - private static final int GROUP_DO_NOTHING = 253; - private static final int GROUP_CWID = 254; - private static final int GROUP_CHANNEL_IDLE = 255; - - private static final int FREE_ALL_CHANNELS_BUSY = 0; - private static final int FREE_LTR_GROUP_CALL = 30; - private static final int FREE_LTRNET_GROUP_CALL = 31; - - private LTRNetOSWMessage mAuxMessage; - - public LTRNetOSWMessage( BinaryMessage message, AliasList list ) - { - super( message, MessageDirection.OSW, list ); - mMessageType = getType(); - } - - public void setAuxiliaryMessage( LTRNetOSWMessage message ) - { - if( message != null ) - { - mAuxMessage = message; - } - } - - public String getRadioUniqueIDText() - { - StringBuilder sb = new StringBuilder(); - - int radioId = getRadioUniqueID(); - - if( radioId != INT_NULL_VALUE ) - { - sb.append( " RADIO UNIQUE ID: " ); - sb.append( radioId ); - } - - return sb.toString(); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append( mDatestampFormatter.format( - new Date( System.currentTimeMillis() ) ) ); - sb.append( " LTRNet OSW [" ); - sb.append( mCRC.getAbbreviation() ); - sb.append( "] " ); - sb.append( getMessage() ); - pad( sb, 100 ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - switch( mMessageType ) - { - case SY_IDLE: - sb.append( "IDLE AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " HOME:" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " GRP:" ); - sb.append( format( getGroup(), 3 ) ); - sb.append( " FREE:" ); - sb.append( format( getFree(), 2 ) ); - break; - case CA_STRT: - sb.append( "CALL LCN:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " TG [ " ); - sb.append( getTalkgroupID() ); - sb.append( "/" ); - sb.append( getTalkgroupIDAlias() ); - sb.append( " ] FREE:" ); - sb.append( format( getFree(), 2 ) ); - break; - case CA_ENDD: - sb.append( "END* AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " TG [" ); - sb.append( getTalkgroupID() ); - sb.append( "/" ); - sb.append( getTalkgroupIDAlias() ); - sb.append( "] FREE:" ); - sb.append( format( getFree(), 2 ) ); - break; - case MA_CHNL: - sb.append( "CHAN MAP LOW LCN [" ); - sb.append( getChannelMapAsString( getChannelMapLow() ) ); - sb.append( "]" ); - break; - case MA_CHNH: - sb.append( "CHAN MAP HIGH LCN [" ); - sb.append( getChannelMapAsString( getChannelMapHigh() ) ); - sb.append( "]" ); - break; - case FQ_RXLO: - sb.append( "CHAN RX LOW LCN:" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " = " ); - long rxFreqLo = getFrequency(); - if( rxFreqLo > 0 ) - { - sb.append( mDecimalFormatter.format( (double)rxFreqLo / 1E6d ) ); - } - else - { - sb.append( "000.00000" ); - } - sb.append( " MHz" ); - break; - case FQ_RXHI: - sb.append( "CHAN RX HIGH LCN:" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " = " ); - long rxfreqhi = getFrequency(); - if( rxfreqhi > 0 ) - { - sb.append( mDecimalFormatter.format( (double)rxfreqhi / 1E6d ) ); - } - else - { - sb.append( "000.00000" ); - } - sb.append( " MHz" ); - break; - case FQ_TXHI: - sb.append( "CHAN TX LOW LCN:" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " = " ); - long txfreq = getFrequency(); - if( txfreq > 0 ) - { - sb.append( mDecimalFormatter.format( (double)txfreq / 1E6d ) ); - } - else - { - sb.append( "000.00000" ); - } - sb.append( " MHz" ); - break; - case FQ_TXLO: - sb.append( "CHAN TX HIGH LCN:" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " = " ); - long txFreqLo = getFrequency(); - if( txFreqLo > 0 ) - { - sb.append( mDecimalFormatter.format( (double)txFreqLo / 1E6d ) ); - } - else - { - sb.append( "000.00000" ); - } - sb.append( " MHz" ); - break; - case ID_SITE: - String siteId = getSiteID(); - if( siteId != null ) - { - sb.append( "SITE ID: " ); - sb.append( siteId ); - sb.append( " " ); - sb.append( getSiteIDAlias() ); - } - break; - case ID_NBOR: - int neighborRank = getNeighborRank(); - String neighborId = getNeighborID(); - - if( neighborRank != INT_NULL_VALUE && - neighborId != null ) - { - sb.append( "NEIGHBOR SITE " ); - sb.append( neighborRank ); - sb.append( ": " ); - sb.append( neighborId ); - sb.append( " " ); - sb.append( getNeighborIDAlias() ); - } - break; - case ID_UNIQ: - sb.append( "REGISTER RADIO UNIQUE ID:" ); - sb.append( getRadioUniqueID() ); - break; - default: - sb.append( "UNKNOWN " ); - sb.append( getArea() ); - sb.append( " " + format( getChannel(), 2 ) ); - sb.append( " " + format( getHomeRepeater(), 2 ) ); - sb.append( " " + format( getGroup(), 3 ) ); - sb.append( " " + format( getFree(), 2 ) ); - sb.append( " A:" ); - sb.append( getArea() ); - sb.append( " C:" ); - sb.append( format( getChannel(), 2 ) ); - sb.append( " H:" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( " G:" ); - sb.append( format( getGroup(), 3 ) ); - sb.append( " F:" ); - sb.append( format( getFree(), 2 ) ); - } - - return sb.toString(); - } - - public String getTalkgroupID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getArea() ); - sb.append( "-" ); - sb.append( format( getHomeRepeater(), 2 ) ); - sb.append( "-" ); - sb.append( format( getGroup(), 3 ) ); - - return sb.toString(); - } - - public Alias getTalkgroupIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getTalkgroupID() ); - } - - return null; - } - - public ArrayList getChannelMapLow() - { - ArrayList retVal = new ArrayList(); - - if( mMessageType == MessageType.MA_CHNL ) - { - for( int x = 27; x >= 18; x-- ) - { - if( mMessage.get( x ) ) - { - retVal.add( 28 - x ); - } - } - } - - return retVal; - } - - public ArrayList getChannelMapHigh() - { - ArrayList retVal = new ArrayList(); - - if( mMessageType == MessageType.MA_CHNH ) - { - for( int x = 27; x >= 18; x-- ) - { - if( mMessage.get( x ) ) - { - retVal.add( 38 - x ); - } - } - } - - return retVal; - } - - public String getChannelMapAsString( ArrayList channels ) - { - StringBuilder sb = new StringBuilder(); - - for( Integer channel: channels ) - { - sb.append( channel ); - sb.append( " " ); - } - - return sb.toString(); - } - - public int getRadioUniqueID() - { - int retVal = INT_NULL_VALUE; - - if( mMessageType == MessageType.ID_UNIQ ) - { - retVal = mMessage.getInt( 17, 32 ); - } - - return retVal; - } - - public Alias getRadioUniqueIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getUniqueID( getRadioUniqueID() ); - } - - return null; - } - - public String getSiteID() - { - if( mMessageType == MessageType.ID_SITE ) - { - return String.valueOf( mMessage.getInt( 23, 32 ) ); - } - - return null; - } - - public Alias getSiteIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getSiteID( getSiteID() ); - } - - return null; - } - - public String getNeighborID() - { - if( mMessageType == MessageType.ID_NBOR ) - { - int id = mMessage.getInt( 23, 32 ); - - if( id > 0 ) - { - return String.valueOf( id ); - } - } - - return null; - } - - private Alias getNeighborIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getSiteID( getNeighborID() ); - } - - return null; - } - - public int getNeighborRank() - { - int retVal = INT_NULL_VALUE; - - if( mMessageType == MessageType.ID_NBOR ) - { - retVal = mMessage.getInt( 15, 18 ) + 1; - } - - return retVal; - } - - public long getFrequency() - { - long retVal = 0; - - if( mAuxMessage != null ) - { - int high; - int low; - - MessageType type = getMessageType(); - - if( type == MessageType.FQ_RXHI || type == MessageType.FQ_TXHI ) - { - high = getHighChannelUnits(); - low = mAuxMessage.getLowChannelUnits(); - } - else - { - high = mAuxMessage.getHighChannelUnits(); - low = getLowChannelUnits(); - } - - if( high != INT_NULL_VALUE && low != INT_NULL_VALUE ) - { - retVal = ( high + low ) * 1250 + 150000000; - } - } - - return retVal; - } - - /** - * Returns the value of the high order (15 - 12) frequency bits which are - * the lower 4 bits of the Free field, left shifted by 12, representing the - * frequency value in units of .00125 MHz - */ - public int getHighChannelUnits() - { - int retVal = INT_NULL_VALUE; - - if( mAuxMessage != null && - ( mMessageType == MessageType.FQ_RXHI || - mMessageType == MessageType.FQ_TXHI ) ) - { - retVal = mMessage.getInt( 29, 32 ); - - retVal = Integer.rotateLeft( retVal, 12 ); - } - - return retVal; - } - - /** - * Returns the value of the low order (11 - 0) frequency bits which are - * contained in the Group and Free fields representing the frequency value - * units of .00125 MHz - */ - public int getLowChannelUnits() - { - int retVal = INT_NULL_VALUE; - - if( mMessageType == MessageType.FQ_RXLO || - mMessageType == MessageType.FQ_TXLO ) - { - retVal = mMessage.getInt( 21, 32 ); - } - - return retVal; - } - - public MessageType getType() - { - MessageType retVal = MessageType.UN_KNWN; - - int channel = getChannel(); - - int home = getHomeRepeater(); - - //LTR-Net messages - if( home != 31 && ( channel > 20 || home > 20 ) ) - { - switch( channel ) - { - case CHIU_UNIQUE_ID: - retVal = MessageType.ID_UNIQ; - break; - case CHIU_SITE_ID: - retVal = MessageType.ID_SITE; - break; - case CHIU_TRANSMIT_FREQUENCY: - if( getHomeRepeater() == 24 ) - { - retVal = MessageType.ID_UNIQ; - } - else - { - if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) - { - retVal = MessageType.FQ_TXHI; - } - else - { - retVal = MessageType.FQ_TXLO; - } - } - break; - case CHIU_RECEIVE_FREQUENCY: - if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) - { - retVal = MessageType.FQ_RXHI; - } - else - { - retVal = MessageType.FQ_RXLO; - } - break; - case CHIU_NEIGHBOR: - retVal = MessageType.ID_NBOR; - break; - case CHIU_MAP: - if( mMessage.get( CHANNEL_MAP_MESSAGE_TYPE_BIT ) ) - { - retVal = MessageType.MA_CHNH; - } - else - { - retVal = MessageType.MA_CHNL; - } - break; - case CHIU_CALL_END: - retVal = MessageType.CA_ENDD; - } - } - else - { - int group = getGroup(); - - if( group == GROUP_CHANNEL_IDLE ) - { - retVal = MessageType.SY_IDLE; - } - else - { - retVal = MessageType.CA_STRT; - } - } - - return retVal; - } - - @Override - public String getFromID() - { - if( mMessageType == MessageType.CA_STRT || - mMessageType == MessageType.CA_ENDD ) - { - return getTalkgroupID(); - } - else - { - return null; - } - } - - @Override - public Alias getFromIDAlias() - { - if( mMessageType == MessageType.CA_STRT || - mMessageType == MessageType.CA_ENDD ) - { - return getTalkgroupIDAlias(); - } - else - { - return null; - } - } - - @Override - public Plottable getPlottable() - { - // TODO Auto-generated method stub - return null; - } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - Alias neighbor = getNeighborIDAlias(); - - if( neighbor != null ) - { - aliases.add( neighbor ); - } - - Alias uid = getRadioUniqueIDAlias(); - - if( uid != null ) - { - aliases.add( uid ); - } - - Alias site = getSiteIDAlias(); - - if( site != null ) - { - aliases.add( site ); - } - - return aliases; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LtrNetMessageType.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LtrNetMessageType.java new file mode 100644 index 00000000..b9a25707 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LtrNetMessageType.java @@ -0,0 +1,46 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet; + +public enum LtrNetMessageType +{ + ISW_CALL_END, + ISW_CALL_START, + ISW_REGISTRATION_REQUEST_ESN_HIGH, + ISW_REGISTRATION_REQUEST_ESN_LOW, + ISW_REQUEST_ACCESS, + ISW_UNIQUE_ID, + ISW_UNKNOWN, + + OSW_CALL_END, + OSW_CALL_START, + OSW_CHANNEL_MAP_HIGH, + OSW_CHANNEL_MAP_LOW, + OSW_SYSTEM_IDLE, + OSW_NEIGHBOR_ID, + OSW_RECEIVE_FREQUENCY_HIGH, + OSW_RECEIVE_FREQUENCY_LOW, + OSW_REGISTRATION_ACCEPT, + OSW_SITE_ID, + OSW_TRANSMIT_FREQUENCY_HIGH, + OSW_TRANSMIT_FREQUENCY_LOW, + OSW_UNKNOWN +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/channel/LtrNetChannel.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/channel/LtrNetChannel.java new file mode 100644 index 00000000..df753ccf --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/channel/LtrNetChannel.java @@ -0,0 +1,111 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.channel; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.protocol.Protocol; + +public class LtrNetChannel implements IChannelDescriptor, Comparable +{ + private long mDownlink; + private long mUplink; + private int mChannel; + + public LtrNetChannel(int channel) + { + mChannel = channel; + } + + public int getChannel() + { + return mChannel; + } + + public void setDownlink(long downlink) + { + mDownlink = downlink; + } + + public void setUplink(long uplink) + { + mUplink = uplink; + } + + @Override + public long getDownlinkFrequency() + { + return mDownlink; + } + + @Override + public long getUplinkFrequency() + { + return mUplink; + } + + @Override + public int[] getFrequencyBandIdentifiers() + { + return new int[0]; + } + + @Override + public void setFrequencyBand(IFrequencyBand bandIdentifier) + { + //No-op + } + + @Override + public boolean isTDMAChannel() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 0; + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_NET; + } + + @Override + public String toString() + { + return String.valueOf(getChannel()); + } + + public String description() + { + return "TRANSMIT: " + getDownlinkFrequency() + " RECEIVE:" + getUplinkFrequency(); + } + + @Override + public int compareTo(LtrNetChannel o) + { + return Integer.compare(getChannel(), o.getChannel()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/NeighborIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/NeighborIdentifier.java new file mode 100644 index 00000000..54882ec6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/NeighborIdentifier.java @@ -0,0 +1,46 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class NeighborIdentifier extends IntegerIdentifier +{ + public NeighborIdentifier(int site) + { + super(site, IdentifierClass.NETWORK, Form.NEIGHBOR_SITE, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_NET; + } + + public static NeighborIdentifier create(int site) + { + return new NeighborIdentifier(site); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/SiteIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/SiteIdentifier.java new file mode 100644 index 00000000..983cc6fb --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/SiteIdentifier.java @@ -0,0 +1,46 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class SiteIdentifier extends IntegerIdentifier +{ + public SiteIdentifier(int site) + { + super(site, IdentifierClass.NETWORK, Form.SITE, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_NET; + } + + public static SiteIdentifier create(int site) + { + return new SiteIdentifier(site); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/UniqueIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/UniqueIdentifier.java new file mode 100644 index 00000000..b0d53d53 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/identifier/UniqueIdentifier.java @@ -0,0 +1,66 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Unique ID or radio ID for LTR-Net radios is a 16-bit logical network radio identifier value. + */ +public class UniqueIdentifier extends IntegerIdentifier implements Comparable +{ + public UniqueIdentifier(int value, Role role) + { + super(value, IdentifierClass.USER, Form.UNIQUE_ID, role); + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_NET; + } + + /** + * Creates a unique identifier for the FROM radio role + */ + public static UniqueIdentifier createFrom(int value) + { + return new UniqueIdentifier(value, Role.FROM); + } + + /** + * Creates a unique identifier for the TO radio role + */ + public static UniqueIdentifier createTo(int value) + { + return new UniqueIdentifier(value, Role.TO); + } + + @Override + public int compareTo(UniqueIdentifier o) + { + return getValue().compareTo(o.getValue()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java new file mode 100644 index 00000000..8f6b9a83 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java @@ -0,0 +1,116 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.edac.CRCLTR; +import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup; +import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.protocol.Protocol; + +public abstract class LtrNetMessage extends Message +{ + protected static final int[] SYNC = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + protected static final int[] AREA = {9}; + protected static final int[] CHANNEL = {10, 11, 12, 13, 14}; + protected static final int[] HOME_REPEATER = {15, 16, 17, 18, 19}; + protected static final int[] GROUP = {20, 21, 22, 23, 24, 25, 26, 27}; + protected static final int[] FREE = {28, 29, 30, 31, 32}; + protected static final int[] CRC_FIELD = {33, 34, 35, 36, 37, 38, 39}; + protected static final int[] SIXTEEN_BITS = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; + + protected CorrectedBinaryMessage mMessage; + protected CRC mCRC; + private LTRTalkgroup mTalkgroup; + + public LtrNetMessage(CorrectedBinaryMessage message, MessageDirection direction, long timestamp) + { + super(timestamp); + mMessage = message; + mCRC = CRCLTR.check(message, direction); + } + + /** + * Underlying binary message + */ + protected CorrectedBinaryMessage getMessage() + { + return mMessage; + } + + public abstract LtrNetMessageType getLtrNetMessageType(); + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_NET; + } + + /** + * Talkgroup identifier + */ + public LTRTalkgroup getTalkgroup() + { + if(mTalkgroup == null) + { + mTalkgroup = LTRTalkgroup.create((getHomeRepeater(getMessage()) << 8) + getGroup(getMessage())); + } + + return mTalkgroup; + } + + public boolean isValid() + { + return mCRC.passes(); + } + + public CRC getCRC() + { + return mCRC; + } + + public static int getArea(CorrectedBinaryMessage message) + { + return message.getInt(AREA); + } + + public static int getChannel(CorrectedBinaryMessage message) + { + return message.getInt(CHANNEL); + } + + public static int getHomeRepeater(CorrectedBinaryMessage message) + { + return message.getInt(HOME_REPEATER); + } + + public static int getGroup(CorrectedBinaryMessage message) + { + return message.getInt(GROUP); + } + + public static int getFree(CorrectedBinaryMessage message) + { + return message.getInt(FREE); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessageFactory.java new file mode 100644 index 00000000..ddfd8c48 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessageFactory.java @@ -0,0 +1,119 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswCallEnd; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswCallStart; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswUniqueId; +import io.github.dsheirer.module.decode.ltrnet.message.isw.IswUnknown; +import io.github.dsheirer.module.decode.ltrnet.message.isw.LtrNetIswMessage; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RegistrationRequestEsnHigh; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RegistrationRequestEsnLow; +import io.github.dsheirer.module.decode.ltrnet.message.isw.RequestAccess; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ChannelMapHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ChannelMapLow; +import io.github.dsheirer.module.decode.ltrnet.message.osw.LtrNetOswMessage; +import io.github.dsheirer.module.decode.ltrnet.message.osw.NeighborId; +import io.github.dsheirer.module.decode.ltrnet.message.osw.OswCallEnd; +import io.github.dsheirer.module.decode.ltrnet.message.osw.OswCallStart; +import io.github.dsheirer.module.decode.ltrnet.message.osw.OswUnknown; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ReceiveFrequencyHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.ReceiveFrequencyLow; +import io.github.dsheirer.module.decode.ltrnet.message.osw.RegistrationAccept; +import io.github.dsheirer.module.decode.ltrnet.message.osw.SiteId; +import io.github.dsheirer.module.decode.ltrnet.message.osw.SystemIdle; +import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh; +import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow; + +/** + * Message factory for creating LTR-Net messages + */ +public class LtrNetMessageFactory +{ + public static LtrNetMessage create(MessageDirection messageDirection, CorrectedBinaryMessage message, long timestamp) + { + switch(messageDirection) + { + case ISW: + //Flip all of the message bits since ISW messages are inverted + message.flip(0, 40); + + LtrNetMessageType iswType = LtrNetIswMessage.getMessageType(message); + + switch(iswType) + { + case ISW_CALL_END: + return new IswCallEnd(message, timestamp); + case ISW_CALL_START: + return new IswCallStart(message, timestamp); + case ISW_REGISTRATION_REQUEST_ESN_HIGH: + return new RegistrationRequestEsnHigh(message, timestamp); + case ISW_REGISTRATION_REQUEST_ESN_LOW: + return new RegistrationRequestEsnLow(message, timestamp); + case ISW_REQUEST_ACCESS: + return new RequestAccess(message, timestamp); + case ISW_UNIQUE_ID: + return new IswUniqueId(message, timestamp); + case ISW_UNKNOWN: + default: + return new IswUnknown(message, timestamp); + } + case OSW: + LtrNetMessageType oswType = LtrNetOswMessage.getMessageType(message); + + switch(oswType) + { + case OSW_CALL_END: + return new OswCallEnd(message, timestamp); + case OSW_CALL_START: + return new OswCallStart(message, timestamp); + case OSW_CHANNEL_MAP_HIGH: + return new ChannelMapHigh(message, timestamp); + case OSW_CHANNEL_MAP_LOW: + return new ChannelMapLow(message, timestamp); + case OSW_SYSTEM_IDLE: + return new SystemIdle(message, timestamp); + case OSW_NEIGHBOR_ID: + return new NeighborId(message, timestamp); + case OSW_RECEIVE_FREQUENCY_HIGH: + return new ReceiveFrequencyHigh(message, timestamp); + case OSW_RECEIVE_FREQUENCY_LOW: + return new ReceiveFrequencyLow(message, timestamp); + case OSW_REGISTRATION_ACCEPT: + return new RegistrationAccept(message, timestamp); + case OSW_SITE_ID: + return new SiteId(message, timestamp); + case OSW_TRANSMIT_FREQUENCY_HIGH: + return new TransmitFrequencyHigh(message, timestamp); + case OSW_TRANSMIT_FREQUENCY_LOW: + return new TransmitFrequencyLow(message, timestamp); + case OSW_UNKNOWN: + default: + return new OswUnknown(message, timestamp); + } + } + + return null; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallEnd.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallEnd.java new file mode 100644 index 00000000..839a8b7e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallEnd.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call End Message. + */ +public class IswCallEnd extends LtrNetIswMessage +{ + private List mIdentifiers; + + /** + * Constructs a message + */ + public IswCallEnd(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_CALL_END; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CALL END TALKGROUP:").append(getTalkgroup().formatted()); + sb.append(" AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + /** + * LCN for the call + */ + public int getChannel() + { + return getChannel(getMessage()); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallStart.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallStart.java new file mode 100644 index 00000000..28717bf5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswCallStart.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call Start Message. + */ +public class IswCallStart extends LtrNetIswMessage +{ + private List mIdentifiers; + + /** + * Constructs a message + */ + public IswCallStart(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_CALL_START; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CALL START TALKGROUP:").append(getTalkgroup().formatted()); + sb.append(" AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + /** + * LCN for the call + */ + public int getChannel() + { + return getChannel(getMessage()); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUniqueId.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUniqueId.java new file mode 100644 index 00000000..ce2b3f76 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUniqueId.java @@ -0,0 +1,83 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.identifier.UniqueIdentifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * ESN Low Message. ESN is transmitted over two messages with the ESNHigh message containing the high-order 16 bits + * and the ESNLow message containing the low-order 16 bits. + */ +public class IswUniqueId extends LtrNetIswMessage +{ + private UniqueIdentifier mUniqueIdentifier; + private List mIdentifiers; + + /** + * Constructs an ESN-High message + */ + public IswUniqueId(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_UNIQUE_ID; + } + + public UniqueIdentifier getUniqueID() + { + if(mUniqueIdentifier == null) + { + mUniqueIdentifier = UniqueIdentifier.createFrom(getMessage().getInt(SIXTEEN_BITS)); + } + + return mUniqueIdentifier; + } + + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("RADIO UNIQUE ID: ").append(getUniqueID()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getUniqueID()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUnknown.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUnknown.java new file mode 100644 index 00000000..77e6ff49 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/IswUnknown.java @@ -0,0 +1,65 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +/** + * ISW Unknown Message. + */ +public class IswUnknown extends LtrNetIswMessage +{ + /** + * Constructs a message + */ + public IswUnknown(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_UNKNOWN; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ISW UNKNOWN MESSAGE - AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" HOME:").append(getHomeRepeater(getMessage())); + sb.append(" GROUP").append(getGroup(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java new file mode 100644 index 00000000..3771e7b0 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java @@ -0,0 +1,93 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.edac.CRCLTR; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage; + +public abstract class LtrNetIswMessage extends LtrNetMessage +{ + /** + * LTR-Net Inbound (mobile - to tower) Message + */ + public LtrNetIswMessage(CorrectedBinaryMessage message, long timestamp) + { + super(message, MessageDirection.ISW, timestamp); + + /** + * If the CRC fails, test for a transmitted CRC of 127 and then check + * the Free field for special messages + */ + if(mCRC == CRC.FAILED_CRC && CRCLTR.getTransmittedChecksum(mMessage) == 127) + { + int free = getFree(getMessage()); + + if(free == 31 || free == 23) + { + mCRC = CRC.PASSED; + } + } + } + + /** + * Determines the ISW message type + */ + public static LtrNetMessageType getMessageType(CorrectedBinaryMessage message) + { + int channel = getChannel(message); + + if(channel == 31) + { + return LtrNetMessageType.ISW_CALL_END; + } + else if(channel > 20) + { + switch(channel) + { + case 24: + return LtrNetMessageType.ISW_UNIQUE_ID; + case 27: + return LtrNetMessageType.ISW_REGISTRATION_REQUEST_ESN_LOW; + case 29: + return LtrNetMessageType.ISW_REGISTRATION_REQUEST_ESN_HIGH; + } + } + else if(channel > 0) + { + int free = getFree(message); + + switch(free) + { + case 21: + return LtrNetMessageType.ISW_CALL_START; + case 23: + return LtrNetMessageType.ISW_CALL_END; + case 31: + return LtrNetMessageType.ISW_REQUEST_ACCESS; + } + } + + return LtrNetMessageType.ISW_UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnHigh.java new file mode 100644 index 00000000..975df959 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnHigh.java @@ -0,0 +1,113 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.esn.ESNIdentifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.protocol.Protocol; + +import java.util.ArrayList; +import java.util.List; + +/** + * ESN High Message. ESN is transmitted over two messages with the ESNHigh message containing the high-order 16 bits + * and the ESNLow message containing the low-order 16 bits. + */ +public class RegistrationRequestEsnHigh extends LtrNetIswMessage +{ + private RegistrationRequestEsnLow mRegistrationRequestEsnLow; + private ESNIdentifier mESNIdentifier; + private List mIdentifiers; + + /** + * Constructs an ESN-High message + */ + public RegistrationRequestEsnHigh(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_REGISTRATION_REQUEST_ESN_HIGH; + } + + /** + * Sets the corresponding ESN low message that can be used to create the full ESN value + */ + public void setEsnLowMessage(RegistrationRequestEsnLow message) + { + if(message != null) + { + mRegistrationRequestEsnLow = message; + } + } + + public boolean isCompleteEsn() + { + return mRegistrationRequestEsnLow != null; + } + + /** + * ESN Identifier with at least the high-order 16-bits specified and optionally includes the low-order 16 bits. + */ + public ESNIdentifier getESN() + { + if(mESNIdentifier == null) + { + mESNIdentifier = ESNIdentifier.create(getESNHigh() + + (mRegistrationRequestEsnLow != null ? mRegistrationRequestEsnLow.getESNLow() : "xxxx"), + Protocol.LTR_NET, Role.FROM); + } + + return mESNIdentifier; + } + + public String getESNHigh() + { + int esnHigh = getMessage().getInt(SIXTEEN_BITS); + + return String.format("%04X", esnHigh & 0xFFFF); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ESN HIGH: ").append(getESN()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getESN()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnLow.java new file mode 100644 index 00000000..09e88b03 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RegistrationRequestEsnLow.java @@ -0,0 +1,112 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.esn.ESNIdentifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.protocol.Protocol; + +import java.util.ArrayList; +import java.util.List; + +/** + * ESN Low Message. ESN is transmitted over two messages with the ESNHigh message containing the high-order 16 bits + * and the ESNLow message containing the low-order 16 bits. + */ +public class RegistrationRequestEsnLow extends LtrNetIswMessage +{ + private RegistrationRequestEsnHigh mRegistrationRequestEsnHigh; + private ESNIdentifier mESNIdentifier; + private List mIdentifiers; + + /** + * Constructs an ESN-High message + */ + public RegistrationRequestEsnLow(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_REGISTRATION_REQUEST_ESN_LOW; + } + + /** + * Sets the corresponding ESN high message that can be used to create the full ESN value + */ + public void setESNHighMessage(RegistrationRequestEsnHigh message) + { + if(message != null) + { + mRegistrationRequestEsnHigh = message; + } + } + + public boolean isCompleteEsn() + { + return mRegistrationRequestEsnHigh != null; + } + + /** + * ESN Identifier with at least the high-order 16-bits specified and optionally includes the low-order 16 bits. + */ + public ESNIdentifier getESN() + { + if(mESNIdentifier == null) + { + mESNIdentifier = ESNIdentifier.create((mRegistrationRequestEsnHigh != null ? mRegistrationRequestEsnHigh.getESNHigh() : "xxxx") + getESNLow(), + Protocol.LTR_NET, Role.FROM); + } + + return mESNIdentifier; + } + + public String getESNLow() + { + int esnLow = getMessage().getInt(SIXTEEN_BITS); + + return String.format("%04X", esnLow & 0xFFFF); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ESN LOW: ").append(getESN()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getESN()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RequestAccess.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RequestAccess.java new file mode 100644 index 00000000..c9c7edbe --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/RequestAccess.java @@ -0,0 +1,72 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.isw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Request access Message. + */ +public class RequestAccess extends LtrNetIswMessage +{ + private List mIdentifiers; + + /** + * Constructs a message + */ + public RequestAccess(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.ISW_REQUEST_ACCESS; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("REQUEST ACCESS TALKGROUP:").append(getTalkgroup()); + sb.append(" AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java new file mode 100644 index 00000000..6a7e95e2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java @@ -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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Channel Map - High Channels (11 - 20). + */ +public class ChannelMapHigh extends LtrNetOswMessage +{ + /** + * Constructs a message + */ + public ChannelMapHigh(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_CHANNEL_MAP_HIGH; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CHANNEL MAP HIGH ").append(getChannels()); + return sb.toString(); + } + + public List getChannels() + { + List retVal = new ArrayList<>(); + + for(int x = 27; x >= 18; x--) + { + if(getMessage().get(x)) + { + retVal.add(38 - x); + } + } + + return retVal; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java new file mode 100644 index 00000000..3ffddd0e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java @@ -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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Channel Map - Low Channels (1 - 10). + */ +public class ChannelMapLow extends LtrNetOswMessage +{ + /** + * Constructs a message + */ + public ChannelMapLow(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_CHANNEL_MAP_LOW; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CHANNEL MAP LOW ").append(getChannels()); + return sb.toString(); + } + + public List getChannels() + { + List retVal = new ArrayList<>(); + + for(int x = 27; x >= 18; x--) + { + if(getMessage().get(x)) + { + retVal.add(28 - x); + } + } + + return retVal; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/Frequency.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/Frequency.java new file mode 100644 index 00000000..7580744f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/Frequency.java @@ -0,0 +1,63 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Base frequency message + */ +public abstract class Frequency extends LtrNetOswMessage +{ + /** + * Constructs a message + */ + public Frequency(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + protected abstract FrequencyHigh getFrequencyHigh(); + protected abstract FrequencyLow getFrequencyLow(); + + protected abstract boolean hasFrequencyHigh(); + protected abstract boolean hasFrequencyLow(); + + /** + * Channel number for the frequency + */ + public int getChannel() + { + return getHomeRepeater(getMessage()); + } + + /** + * Frequency for the channel when both the high and low frequency messages are available + */ + public long getFrequency() + { + if(hasFrequencyHigh() && hasFrequencyLow()) + { + return 150000000 + ((getFrequencyHigh().getHighChannelUnits() + getFrequencyLow().getLowChannelUnits()) * 1250); + } + + return 0; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyHigh.java new file mode 100644 index 00000000..e65fabf0 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyHigh.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Base frequency message + */ +public abstract class FrequencyHigh extends Frequency +{ + private FrequencyLow mFrequencyLow; + + /** + * Constructs a message + */ + public FrequencyHigh(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + /** + * Sets the corresponding low frequency message so that the correct frequency can be calculated + */ + public void setFrequencyLow(FrequencyLow frequencyLow) + { + mFrequencyLow = frequencyLow; + } + + /** + * Returns the value of the high order (15 - 12) frequency bits which are + * the lower 4 bits of the Free field, left shifted by 12, representing the + * frequency value in units of .00125 MHz + */ + public int getHighChannelUnits() + { + return Integer.rotateLeft(getMessage().getInt(29, 32), 12); + } + + @Override + protected FrequencyHigh getFrequencyHigh() + { + return this; + } + + @Override + protected FrequencyLow getFrequencyLow() + { + return mFrequencyLow; + } + + @Override + protected boolean hasFrequencyHigh() + { + return true; + } + + @Override + protected boolean hasFrequencyLow() + { + return mFrequencyLow != null; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyLow.java new file mode 100644 index 00000000..19d97bad --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/FrequencyLow.java @@ -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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Base frequency message + */ +public abstract class FrequencyLow extends Frequency +{ + private FrequencyHigh mFrequencyHigh; + + /** + * Constructs a message + */ + public FrequencyLow(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + public void setFrequencyHigh(FrequencyHigh frequencyHigh) + { + mFrequencyHigh = frequencyHigh; + } + + /** + * Returns the value of the low order (11 - 0) frequency bits which are + * contained in the Group and Free fields representing the frequency value + * units of .00125 MHz + */ + public int getLowChannelUnits() + { + return getMessage().getInt(21, 32); + } + + @Override + protected FrequencyHigh getFrequencyHigh() + { + return mFrequencyHigh; + } + + @Override + protected FrequencyLow getFrequencyLow() + { + return this; + } + + @Override + protected boolean hasFrequencyHigh() + { + return mFrequencyHigh != null; + } + + @Override + protected boolean hasFrequencyLow() + { + return true; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/LtrNetOswMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/LtrNetOswMessage.java new file mode 100644 index 00000000..5818526b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/LtrNetOswMessage.java @@ -0,0 +1,109 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage; + +public abstract class LtrNetOswMessage extends LtrNetMessage +{ + private static final int CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20; + private static final int CHANNEL_MAP_MESSAGE_TYPE_BIT = 17; + private static final int HOME_DIRECTED_GROUP_CALL = 29; + private static final int HOME_CHANNEL_IN_USE_MUTE_AUDIO = 30; + private static final int GROUP_DO_NOTHING = 253; + private static final int GROUP_CWID = 254; + private static final int GROUP_CHANNEL_IDLE = 255; + + private static final int FREE_ALL_CHANNELS_BUSY = 0; + private static final int FREE_LTR_GROUP_CALL = 30; + private static final int FREE_LTRNET_GROUP_CALL = 31; + + public LtrNetOswMessage(CorrectedBinaryMessage message, long timestamp) + { + super(message, MessageDirection.OSW, timestamp); + } + + public static LtrNetMessageType getMessageType(CorrectedBinaryMessage message) + { + int channel = getChannel(message); + int home = getHomeRepeater(message); + + //LTR-Net messages + if(home != 31 && (channel > 20 || home > 20)) + { + switch(channel) + { + case 17: + return LtrNetMessageType.OSW_REGISTRATION_ACCEPT; + case 18: + return LtrNetMessageType.OSW_SITE_ID; + case 24: + if(message.get(CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT)) + { + return LtrNetMessageType.OSW_TRANSMIT_FREQUENCY_HIGH; + } + else + { + return LtrNetMessageType.OSW_TRANSMIT_FREQUENCY_LOW; + } + case 25: + if(message.get(CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT)) + { + return LtrNetMessageType.OSW_RECEIVE_FREQUENCY_HIGH; + } + else + { + return LtrNetMessageType.OSW_RECEIVE_FREQUENCY_LOW; + } + case 26: + return LtrNetMessageType.OSW_NEIGHBOR_ID; + case 28: + if(message.get(CHANNEL_MAP_MESSAGE_TYPE_BIT)) + { + return LtrNetMessageType.OSW_CHANNEL_MAP_HIGH; + } + else + { + return LtrNetMessageType.OSW_CHANNEL_MAP_LOW; + } + case 31: + return LtrNetMessageType.OSW_CALL_END; + } + } + else + { + int group = getGroup(message); + + if(group == GROUP_CHANNEL_IDLE) + { + return LtrNetMessageType.OSW_SYSTEM_IDLE; + } + else + { + return LtrNetMessageType.OSW_CALL_START; + } + } + + return LtrNetMessageType.OSW_UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java new file mode 100644 index 00000000..46dab8d7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java @@ -0,0 +1,87 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.identifier.NeighborIdentifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * Neighbor Identifier. + */ +public class NeighborId extends LtrNetOswMessage +{ + private NeighborIdentifier mNeighborIdentifier; + private List mIdentifiers; + + /** + * Constructs a message + */ + public NeighborId(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_NEIGHBOR_ID; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NEIGHBOR:").append(getNeighborID()); + sb.append(" RANK:").append(getNeighborRank()); + return sb.toString(); + } + + public NeighborIdentifier getNeighborID() + { + if(mNeighborIdentifier == null) + { + mNeighborIdentifier = NeighborIdentifier.create(getMessage().getInt(23, 32)); + } + + return mNeighborIdentifier; + } + + public int getNeighborRank() + { + return getMessage().getInt(15, 18) + 1; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getNeighborID()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java new file mode 100644 index 00000000..07669926 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call End Message. + */ +public class OswCallEnd extends LtrNetOswMessage +{ + private List mIdentifiers; + + /** + * Constructs a message + */ + public OswCallEnd(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_CALL_END; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CALL END TALKGROUP:").append(getTalkgroup().formatted()); + sb.append(" AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + /** + * LCN for the call + */ + public int getChannel() + { + return getChannel(getMessage()); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java new file mode 100644 index 00000000..6ddf3f43 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call Start Message. + */ +public class OswCallStart extends LtrNetOswMessage +{ + private List mIdentifiers; + + /** + * Constructs a message + */ + public OswCallStart(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_CALL_START; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CALL START TALKGROUP:").append(getTalkgroup().formatted()); + sb.append(" AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + /** + * LCN for the call + */ + public int getChannel() + { + return getChannel(getMessage()); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java new file mode 100644 index 00000000..66c662ae --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java @@ -0,0 +1,65 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +/** + * OSW Unknown Message. + */ +public class OswUnknown extends LtrNetOswMessage +{ + /** + * Constructs a message + */ + public OswUnknown(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_UNKNOWN; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("OSW UNKNOWN MESSAGE - AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" HOME:").append(getHomeRepeater(getMessage())); + sb.append(" GROUP").append(getGroup(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java new file mode 100644 index 00000000..e79a8624 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java @@ -0,0 +1,63 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +public class ReceiveFrequencyHigh extends FrequencyHigh +{ + /** + * Constructs a message + * + * @param message + * @param timestamp + */ + public ReceiveFrequencyHigh(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_RECEIVE_FREQUENCY_HIGH; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("LCN:").append(getChannel()); + sb.append(" RECEIVE FREQUENCY:").append(getFrequency()).append(" - HIGH"); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java new file mode 100644 index 00000000..5ec5d75d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java @@ -0,0 +1,60 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +public class ReceiveFrequencyLow extends FrequencyLow +{ + /** + * Constructs a message + */ + public ReceiveFrequencyLow(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_RECEIVE_FREQUENCY_LOW; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("LCN:").append(getChannel()); + sb.append(" RECEIVE FREQUENCY:").append(getFrequency()).append(" - LOW"); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.emptyList(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java new file mode 100644 index 00000000..b63b3afc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java @@ -0,0 +1,82 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.identifier.UniqueIdentifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * Registration and assignment of Unique ID to a radio. + */ +public class RegistrationAccept extends LtrNetOswMessage +{ + private UniqueIdentifier mUniqueIdentifier; + private List mIdentifiers; + + /** + * Constructs an ESN-High message + */ + public RegistrationAccept(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_REGISTRATION_ACCEPT; + } + + public UniqueIdentifier getUniqueID() + { + if(mUniqueIdentifier == null) + { + mUniqueIdentifier = UniqueIdentifier.createTo(getMessage().getInt(SIXTEEN_BITS)); + } + + return mUniqueIdentifier; + } + + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("REGISTRATION ACCEPT - RADIO UNIQUE ID: ").append(getUniqueID()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getUniqueID()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java new file mode 100644 index 00000000..eca264ba --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java @@ -0,0 +1,82 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import io.github.dsheirer.module.decode.ltrnet.identifier.SiteIdentifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * Site Identifier. + */ +public class SiteId extends LtrNetOswMessage +{ + private SiteIdentifier mSiteIdentifier; + private List mIdentifiers; + + /** + * Constructs a message + */ + public SiteId(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_SITE_ID; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("SITE:").append(getSiteID()); + return sb.toString(); + } + + public SiteIdentifier getSiteID() + { + if(mSiteIdentifier == null) + { + mSiteIdentifier = SiteIdentifier.create(getMessage().getInt(23, 32)); + } + + return mSiteIdentifier; + } + + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSiteID()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java new file mode 100644 index 00000000..2e080c36 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java @@ -0,0 +1,65 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +public class SystemIdle extends LtrNetOswMessage +{ + public SystemIdle(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_SYSTEM_IDLE; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("IDLE - AREA:").append(getArea(getMessage())); + sb.append(" LCN:").append(getChannel(getMessage())); + sb.append(" HOME:").append(getHomeRepeater(getMessage())); + sb.append(" GROUP:").append(getGroup(getMessage())); + sb.append(" FREE:").append(getFree(getMessage())); + return sb.toString(); + } + + public int getChannel() + { + return getChannel(getMessage()); + } + + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java new file mode 100644 index 00000000..f662f45e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java @@ -0,0 +1,60 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +public class TransmitFrequencyHigh extends FrequencyHigh +{ + /** + * Constructs a message + */ + public TransmitFrequencyHigh(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_TRANSMIT_FREQUENCY_HIGH; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("LCN:").append(getChannel()); + sb.append(" TRANSMIT FREQUENCY:").append(getFrequency()).append(" - HIGH"); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java new file mode 100644 index 00000000..e5904963 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java @@ -0,0 +1,60 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrnet.message.osw; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; + +import java.util.Collections; +import java.util.List; + +public class TransmitFrequencyLow extends FrequencyLow +{ + /** + * Constructs a message + */ + public TransmitFrequencyLow(CorrectedBinaryMessage message, long timestamp) + { + super(message, timestamp); + } + + @Override + public LtrNetMessageType getLtrNetMessageType() + { + return LtrNetMessageType.OSW_TRANSMIT_FREQUENCY_LOW; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("LCN:").append(getChannel()); + sb.append(" TRANSMIT FREQUENCY:").append(getFrequency()).append(" - LOW"); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java index 212cf5c7..18e585f0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java @@ -53,7 +53,7 @@ public class LTRStandardDecoder extends Decoder implements IReusableBufferListen mLTRDecoder.setMessageFramer(mLTRMessageFramer); mLTRMessageFramer.setSyncDetectListener(mLTRDecoder); - mLTRMessageProcessor = new LTRStandardMessageProcessor(direction, aliasList); + mLTRMessageProcessor = new LTRStandardMessageProcessor(direction); mLTRMessageFramer.addMessageListener(mLTRMessageProcessor); mLTRMessageProcessor.setMessageListener(getMessageListener()); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java index 32d7647c..8d3d6bf5 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,28 +15,30 @@ * * 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.ltrstandard; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier; +import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier; +import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.ltrnet.LTRCallEvent; -import io.github.dsheirer.module.decode.ltrstandard.message.CallEndMessage; -import io.github.dsheirer.module.decode.ltrstandard.message.CallMessage; -import io.github.dsheirer.module.decode.ltrstandard.message.IdleMessage; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.ltrstandard.channel.LtrChannel; +import io.github.dsheirer.module.decode.ltrstandard.message.Call; +import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd; +import io.github.dsheirer.module.decode.ltrstandard.message.Idle; import io.github.dsheirer.module.decode.ltrstandard.message.LTRStandardMessage; +import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,23 +52,17 @@ import java.util.TreeSet; public class LTRStandardDecoderState extends DecoderState { - private final static Logger mLog = - LoggerFactory.getLogger(LTRStandardDecoderState.class); + private final static Logger mLog = LoggerFactory.getLogger(LTRStandardDecoderState.class); - private Map mActiveCalls = new HashMap<>(); - private Set mTalkgroupsFirstHeard = new HashSet<>(); - private Set mTalkgroups = new TreeSet<>(); - - private AliasedStringAttributeMonitor mTalkgroupAttribute; - private long mFrequency; + private Map mActiveCalls = new HashMap<>(); + private Set mTalkgroupsFirstHeard = new HashSet<>(); + private Set mTalkgroups = new TreeSet<>(); private LCNTracker mLCNTracker = new LCNTracker(); + private DecodeEvent mCurrentCallEvent; + private LTRTalkgroup mCurrentTalkgroup; - public LTRStandardDecoderState(AliasList aliasList) + public LTRStandardDecoderState() { - super(aliasList); - - mTalkgroupAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); } @Override @@ -75,116 +72,76 @@ public class LTRStandardDecoderState extends DecoderState } @Override - public void receive(Message message) + public void receive(IMessage message) { if(message.isValid() && message instanceof LTRStandardMessage) { - switch(((LTRStandardMessage) message).getMessageType()) + switch(((LTRStandardMessage)message).getMessageType()) { - case CA_STRT: - CallMessage start = (CallMessage) message; - - int channel = start.getChannel(); - - setChannelNumber(channel); - - mLCNTracker.processFreeChannel(start.getFree()); - - //Only process calls on this LCN, or call detects for - //talkgroups that are homed on this LCN - if(mLCNTracker.isValidChannel(channel) && - (mLCNTracker.isCurrentChannel(channel) || - mLCNTracker.isCurrentChannel(start.getHomeRepeater()))) + case CALL: + if(message instanceof Call) { - LTRCallEvent event = mActiveCalls.get(channel); + Call start = (Call)message; - if(event == null || !event.isMatchingTalkgroup(start.getToID())) + int channel = start.getChannel(); + setChannelNumber(channel); + mLCNTracker.processFreeChannel(start.getFree()); + + //Only process calls or call detects for valid channels + if(mLCNTracker.isValidChannel(channel) && mLCNTracker.isCurrentChannel(channel)) { - //Check for different talkgroup - if(event != null) + if(mCurrentTalkgroup == null || !mCurrentTalkgroup.equals(start.getTalkgroup())) { - event.end(); - mActiveCalls.remove(channel); + mCurrentTalkgroup = start.getTalkgroup(); + getIdentifierCollection().remove(IdentifierClass.USER); + getIdentifierCollection().update(start.getTalkgroup()); + mCurrentCallEvent = DecodeEvent.builder(start.getTimestamp()) + .protocol(Protocol.LTR_STANDARD) + .identifiers(getIdentifierCollection().copyOf()) + .channel(getCurrentChannel()) + .eventDescription("Call") + .build(); } - - boolean current = mLCNTracker.isCurrentChannel(channel); - - event = new LTRCallEvent.Builder( - DecoderType.LTR_STANDARD, - current ? CallEvent.CallEventType.CALL : CallEvent.CallEventType.CALL_DETECT) - .to(start.getToID()) - .aliasList(getAliasList()) - .channel(start.getChannelFormatted()) - .frequency(current ? mFrequency : 0) - .build(); - - mActiveCalls.put(channel, event); - - broadcast(event); - - if(mLCNTracker.isCurrentChannel(channel)) + else { - String talkgroup = start.getToID(); - mTalkgroupAttribute.process(talkgroup); - processTalkgroup(talkgroup); + mCurrentCallEvent.update(start.getTimestamp()); } + broadcast(mCurrentCallEvent); + broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); } break; - case CA_ENDD: - CallEndMessage end = (CallEndMessage) message; - - //Home channel is 31 for call end -- use the free channel - //as the call end channel - int repeater = end.getFree(); - - setChannelNumber(repeater); - - if(mLCNTracker.isCurrentChannel(repeater)) + case CALL_END: + if(message instanceof CallEnd) { - String talkgroup = end.getToID(); - mTalkgroupAttribute.process(talkgroup); - processTalkgroup(talkgroup); + CallEnd end = (CallEnd)message; - LTRCallEvent event = mActiveCalls.remove(repeater); - - if(event != null) + //Home channel is 31 for call end -- use the free channel as the call end channel + int repeater = end.getFree(); + setChannelNumber(repeater); + if(mLCNTracker.isCurrentChannel(repeater)) { - event.end(); - broadcast(event); + if(mCurrentCallEvent != null) + { + mCurrentCallEvent.end(end.getTimestamp()); + } + broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); } } break; - case SY_IDLE: - IdleMessage idle = (IdleMessage) message; - - int lcn = idle.getChannel(); - - mLCNTracker.processCallChannel(lcn); - + case IDLE: + if(message instanceof Idle) + { + mLCNTracker.processCallChannel(((Idle)message).getChannel()); + } break; - case UN_KNWN: default: break; } } } - private void processTalkgroup(String talkgroup) - { - if(mTalkgroupsFirstHeard.contains(talkgroup)) - { - mTalkgroups.add(talkgroup); - } - else - { - mTalkgroupsFirstHeard.add(talkgroup); - } - } - /** * Performs a full reset */ @@ -201,41 +158,25 @@ public class LTRStandardDecoderState extends DecoderState @Override public void start() { - } @Override public void stop() { - } @Override public void init() { - } /** * Performs a temporal reset following a call or other decode event */ - private void resetState() + protected void resetState() { - for(Integer key : mActiveCalls.keySet()) - { - LTRCallEvent event = mActiveCalls.get(key); - - if(event != null) - { - event.end(); - - broadcast(event); - } - } - + super.resetState(); mActiveCalls.clear(); - - mTalkgroupAttribute.reset(); } public boolean hasChannelNumber() @@ -256,8 +197,20 @@ public class LTRStandardDecoderState extends DecoderState if(mLCNTracker.getCurrentChannel() != original) { - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, - "LCN:" + mLCNTracker.getCurrentChannel())); + getIdentifierCollection().update(DecoderLogicalChannelNameIdentifier + .create(String.valueOf(mLCNTracker.getCurrentChannel()), Protocol.LTR_STANDARD)); + + LtrChannel ltrChannel = new LtrChannel(mLCNTracker.getCurrentChannel()); + + Identifier identifier = getIdentifierCollection().getIdentifier(IdentifierClass.CONFIGURATION, + Form.CHANNEL_FREQUENCY, Role.ANY); + + if(identifier instanceof FrequencyConfigurationIdentifier) + { + ltrChannel.setDownlink(((FrequencyConfigurationIdentifier)identifier).getValue()); + } + + setCurrentChannel(ltrChannel); } } @@ -269,11 +222,6 @@ public class LTRStandardDecoderState extends DecoderState case RESET: resetState(); break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - break; - default: - break; } } @@ -321,22 +269,10 @@ public class LTRStandardDecoderState extends DecoderState } else { - for(String talkgroup : mTalkgroups) + for(LTRTalkgroup talkgroup : mTalkgroups) { sb.append(" "); - sb.append(talkgroup); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(talkgroup); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - + sb.append(talkgroup.formatted()); sb.append("\n"); } @@ -425,7 +361,7 @@ public class LTRStandardDecoderState extends DecoderState int count = mFreeLCNCounts[channel]; - int threshold = (int) ((double) mFreeHighestCount * 0.2); + int threshold = (int)((double)mFreeHighestCount * 0.2); return count >= threshold; } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java index c1d9c5ad..17530372 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java @@ -1,57 +1,73 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.ltrstandard; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.message.MessageType; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.ltrstandard.message.LTRStandardMessage; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; -public class LTRStandardMessageFilter extends Filter +public class LTRStandardMessageFilter extends Filter { - private HashMap> mElements = - new HashMap>(); - - public LTRStandardMessageFilter() - { - super( "LTR Message Filter" ); + private Map> mElements = new HashMap<>(); - mElements.put( MessageType.CA_STRT, - new FilterElement( MessageType.CA_STRT ) ); - mElements.put( MessageType.CA_ENDD, - new FilterElement( MessageType.CA_ENDD ) ); - mElements.put( MessageType.SY_IDLE, - new FilterElement( MessageType.SY_IDLE ) ); - } - - @Override - public boolean passes( Message message ) + public LTRStandardMessageFilter() { - if( mEnabled && canProcess( message ) ) - { - LTRStandardMessage ltr = (LTRStandardMessage)message; - - if( mElements.containsKey( ltr.getMessageType() ) ) - { - return mElements.get( ltr.getMessageType() ).isEnabled(); - } - } - - return false; + super("LTR Message Filter"); + + mElements.put(LtrStandardMessageType.CALL, new FilterElement<>(LtrStandardMessageType.CALL)); + mElements.put(LtrStandardMessageType.CALL_END, new FilterElement<>(LtrStandardMessageType.CALL_END)); + mElements.put(LtrStandardMessageType.IDLE, new FilterElement<>(LtrStandardMessageType.IDLE)); + mElements.put(LtrStandardMessageType.UNKNOWN, new FilterElement<>(LtrStandardMessageType.UNKNOWN)); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof LTRStandardMessage; + if(mEnabled && canProcess(message)) + { + LTRStandardMessage ltr = (LTRStandardMessage)message; + + if(mElements.containsKey(ltr.getMessageType())) + { + return mElements.get(ltr.getMessageType()).isEnabled(); + } + } + + return false; } - @Override + public boolean canProcess(IMessage message) + { + return message instanceof LTRStandardMessage; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mElements.values() ); + return new ArrayList>(mElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java index ccf7eb59..b04da8a6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java @@ -1,121 +1,114 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.ltrstandard; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.CRC; import io.github.dsheirer.edac.CRCLTR; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.module.decode.ltrstandard.message.CallEndMessage; -import io.github.dsheirer.module.decode.ltrstandard.message.CallMessage; -import io.github.dsheirer.module.decode.ltrstandard.message.IdleMessage; +import io.github.dsheirer.module.decode.ltrstandard.message.Call; +import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd; +import io.github.dsheirer.module.decode.ltrstandard.message.Idle; import io.github.dsheirer.module.decode.ltrstandard.message.LTRStandardMessage; import io.github.dsheirer.module.decode.ltrstandard.message.UnknownMessage; import io.github.dsheirer.sample.Listener; -public class LTRStandardMessageProcessor implements Listener +public class LTRStandardMessageProcessor implements Listener { - private MessageDirection mDirection; - private AliasList mAliasList; - private Listener mMessageListener; - - /** - * Processes raw binary messages and converts them to the correct message class - * - * @param direction - inbound (ISW) or outbound (OSW) - * @param list - alias list - */ - public LTRStandardMessageProcessor( MessageDirection direction, AliasList list ) - { - mDirection = direction; - mAliasList = list; - } - - @Override - public void receive( BinaryMessage binaryMessage ) + private MessageDirection mDirection; + private Listener mMessageListener; + + /** + * Processes raw binary messages and converts them to the correct message class + * + * @param direction - inbound (ISW) or outbound (OSW) + */ + public LTRStandardMessageProcessor(MessageDirection direction) { - if( mMessageListener != null ) - { - //Inbound Status Word (ISW) is a bit-flipped version of the Outbound - //(OSW), so flip the bits and process it as an OSW - if( mDirection == MessageDirection.ISW ) - { - binaryMessage.flip( 0, 40 ); - } - - CRC crc = CRCLTR.check( binaryMessage, mDirection ); - - if( crc != CRC.FAILED_CRC && - crc != CRC.FAILED_PARITY ) - { - LTRStandardMessage message; - - int channel = binaryMessage.getInt( LTRStandardMessage.CHANNEL ); - int home = binaryMessage.getInt( LTRStandardMessage.HOME_REPEATER ); - int free = binaryMessage.getInt( LTRStandardMessage.FREE ); - int group = binaryMessage.getInt( LTRStandardMessage.GROUP ); - - if( isValidChannel( channel ) && - isValidChannel( home ) && - isValidChannel( free ) ) - { - if( channel == free && group == 255 ) - { - message = new IdleMessage( binaryMessage, mDirection, mAliasList, crc ); - } - else - { - message = new CallMessage( binaryMessage, mDirection, mAliasList, crc ); - } - } - else if( channel == 31 && - isValidChannel( home ) && - isValidChannel( free ) ) - { - message = new CallEndMessage( binaryMessage, mDirection, mAliasList, crc ); - } - else - { - message = new UnknownMessage( binaryMessage, mDirection, mAliasList, crc ); - } - - mMessageListener.receive( message ); - } - } + mDirection = direction; } - - /** - * Checks the channel (LCN) number to ensure it is in the range 1 -20 - */ - private boolean isValidChannel( int channel ) - { - return ( 1 <= channel && channel <= 20 ); - } - - public void setMessageListener( Listener listener ) + + @Override + public void receive(CorrectedBinaryMessage binaryMessage) { - mMessageListener = listener; + if(mMessageListener != null) + { + //Inbound Status Word (ISW) is a bit-flipped version of the Outbound (OSW), so flip the bits and process it + // as an OSW + if(mDirection == MessageDirection.ISW) + { + binaryMessage.flip(0, 40); + } + + CRC crc = CRCLTR.check(binaryMessage, mDirection); + + if(crc.passes()) + { + LTRStandardMessage message; + + int channel = binaryMessage.getInt(LTRStandardMessage.CHANNEL); + int home = binaryMessage.getInt(LTRStandardMessage.HOME_REPEATER); + int free = binaryMessage.getInt(LTRStandardMessage.FREE); + int group = binaryMessage.getInt(LTRStandardMessage.GROUP); + + if(isValidChannel(channel) && isValidChannel(home) && isValidChannel(free)) + { + if(channel == free && group == 255) + { + message = new Idle(binaryMessage, mDirection, crc); + } + else + { + message = new Call(binaryMessage, mDirection, crc); + } + } + else if(channel == 31 && isValidChannel(home) && isValidChannel(free)) + { + message = new CallEnd(binaryMessage, mDirection, crc); + } + else + { + message = new UnknownMessage(binaryMessage, mDirection, crc); + } + + mMessageListener.receive(message); + } + } + } + + /** + * Checks the channel (LCN) number to ensure it is in the range 1 -20 + */ + private boolean isValidChannel(int channel) + { + return (1 <= channel && channel <= 20); + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LtrStandardMessageType.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LtrStandardMessageType.java new file mode 100644 index 00000000..e8c19822 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LtrStandardMessageType.java @@ -0,0 +1,29 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrstandard; + +public enum LtrStandardMessageType +{ + CALL, + CALL_END, + IDLE, + UNKNOWN; +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/channel/LtrChannel.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/channel/LtrChannel.java new file mode 100644 index 00000000..a6767d79 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/channel/LtrChannel.java @@ -0,0 +1,105 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.ltrstandard.channel; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.protocol.Protocol; + +public class LtrChannel implements IChannelDescriptor, Comparable +{ + private long mDownlink; + private int mChannel; + + public LtrChannel(int channel) + { + mChannel = channel; + } + + public int getChannel() + { + return mChannel; + } + + public void setDownlink(long downlink) + { + mDownlink = downlink; + } + + @Override + public long getDownlinkFrequency() + { + return mDownlink; + } + + @Override + public long getUplinkFrequency() + { + return mDownlink; + } + + @Override + public int[] getFrequencyBandIdentifiers() + { + return new int[0]; + } + + @Override + public void setFrequencyBand(IFrequencyBand bandIdentifier) + { + //No-op + } + + @Override + public boolean isTDMAChannel() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 0; + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_STANDARD; + } + + @Override + public String toString() + { + return String.valueOf(getChannel()); + } + + public String description() + { + return "TRANSMIT: " + getDownlinkFrequency(); + } + + @Override + public int compareTo(LtrChannel o) + { + return Integer.compare(getChannel(), o.getChannel()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Call.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Call.java new file mode 100644 index 00000000..316ed12d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Call.java @@ -0,0 +1,67 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrstandard.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType; + +import java.util.ArrayList; +import java.util.List; + +public class Call extends LTRStandardMessage +{ + private List mIdentifiers; + + public Call(CorrectedBinaryMessage message, MessageDirection direction, CRC crc) + { + super(message, direction, crc); + } + + @Override + public LtrStandardMessageType getMessageType() + { + return LtrStandardMessageType.CALL; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CALL TO ").append(getTalkgroup().formatted()); + sb.append(" LCN:").append(getChannel()); + sb.append(" FREE:").append(getFree()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEnd.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEnd.java new file mode 100644 index 00000000..cf0577da --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEnd.java @@ -0,0 +1,67 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrstandard.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType; + +import java.util.ArrayList; +import java.util.List; + +public class CallEnd extends LTRStandardMessage +{ + private List mIdentifiers; + + public CallEnd(CorrectedBinaryMessage message, MessageDirection direction, CRC crc) + { + super(message, direction, crc); + } + + @Override + public LtrStandardMessageType getMessageType() + { + return LtrStandardMessageType.CALL_END; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("CALL END ").append(getTalkgroup().formatted()); + sb.append(" LCN:").append(getChannel()); + sb.append(" FREE:").append(getFree()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEndMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEndMessage.java deleted file mode 100644 index 646f795b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallEndMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.ltrstandard.message; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; - -public class CallEndMessage extends LTRStandardMessage -{ - public CallEndMessage(BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.CA_ENDD; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "END* AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " TG [" ); - sb.append( getToID() ); - sb.append( "/" ); - Alias endAlias = getToIDAlias(); - if( endAlias != null ) - { - sb.append( getToIDAlias().getName() ); - } - else - { - sb.append( "UNKNOWN" ); - } - sb.append( "] FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallMessage.java deleted file mode 100644 index 07f949b5..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/CallMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.ltrstandard.message; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; - -public class CallMessage extends LTRStandardMessage -{ - public CallMessage(BinaryMessage message, MessageDirection direction, AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.CA_STRT; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "CALL LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " TG [ " ); - sb.append( getToID() ); - sb.append( "/" ); - - Alias alias = getToIDAlias(); - - if( alias != null ) - { - sb.append( getToIDAlias().getName() ); - } - else - { - sb.append( "UNKNOWN" ); - } - - sb.append( " ] FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Idle.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Idle.java new file mode 100644 index 00000000..7c9009ae --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/Idle.java @@ -0,0 +1,64 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.ltrstandard.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.MessageDirection; +import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType; + +import java.util.Collections; +import java.util.List; + +/** + * Indicates the repeater is IDLE and available for use + */ +public class Idle extends LTRStandardMessage +{ + public Idle(CorrectedBinaryMessage message, MessageDirection direction, CRC crc) + { + super(message, direction, crc); + } + + @Override + public LtrStandardMessageType getMessageType() + { + return LtrStandardMessageType.IDLE; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("IDLE AREA:").append(getArea()); + sb.append(" LCN:").append(getChannel()); + sb.append(" HOME:").append(getHomeRepeater()); + sb.append(" GRP:").append(getGroup()); + sb.append(" FREE:").append(getFree()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/IdleMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/IdleMessage.java deleted file mode 100644 index d6629e94..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/IdleMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.ltrstandard.message; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; - -public class IdleMessage extends LTRStandardMessage -{ - public IdleMessage(BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.SY_IDLE; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "IDLE AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " HOME:" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( " GRP:" ); - sb.append( getGroupFormatted() ); - sb.append( " FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRStandardMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRStandardMessage.java index d977c3ea..412c13b2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRStandardMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRStandardMessage.java @@ -1,211 +1,148 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.ltrstandard.message; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup; import io.github.dsheirer.message.Message; import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; -import io.github.dsheirer.module.decode.DecoderType; -import org.apache.commons.lang3.StringUtils; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType; +import io.github.dsheirer.protocol.Protocol; +/** + * LTR Standard Base Message + */ public abstract class LTRStandardMessage extends Message { - protected SimpleDateFormat mDatestampFormatter = - new SimpleDateFormat( "yyyyMMdd HHmmss" ); - - public static final int[] SYNC = { 0,1,2,3,4,5,6,7,8 }; - public static final int[] AREA = { 9 }; - public static final int[] CHANNEL = { 10,11,12,13,14 }; - public static final int[] HOME_REPEATER = { 15,16,17,18,19 }; - public static final int[] GROUP = { 20,21,22,23,24,25,26,27 }; - public static final int[] FREE = { 28,29,30,31,32 }; - public static final int[] CHECKSUM = { 33,34,35,36,37,38,39 }; - - protected BinaryMessage mMessage; - protected MessageDirection mMessageDirection; - protected AliasList mAliasList; - protected CRC mCRC; - - public LTRStandardMessage( BinaryMessage message, - MessageDirection direction, - AliasList list, - CRC crc ) + public static final int[] SYNC = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + public static final int[] AREA = {9}; + public static final int[] CHANNEL = {10, 11, 12, 13, 14}; + public static final int[] HOME_REPEATER = {15, 16, 17, 18, 19}; + public static final int[] GROUP = {20, 21, 22, 23, 24, 25, 26, 27}; + public static final int[] FREE = {28, 29, 30, 31, 32}; + public static final int[] CHECKSUM = {33, 34, 35, 36, 37, 38, 39}; + + private CorrectedBinaryMessage mMessage; + private MessageDirection mMessageDirection; + private CRC mCRC; + private LTRTalkgroup mTalkgroup; + + /** + * Constructs the message + * @param message containing the raw bits + * @param direction of the messsage, ISW or OSW + * @param crc error check + */ + public LTRStandardMessage(CorrectedBinaryMessage message, MessageDirection direction, CRC crc) { mMessage = message; mMessageDirection = direction; - mAliasList = list; mCRC = crc; } - - public abstract MessageType getMessageType(); - public boolean isValid() + /** + * Identifies the type of message + */ + public abstract LtrStandardMessageType getMessageType(); + + /** + * Indicates if this message passes the CRC check + */ + public boolean isValid() { - return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY; - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append( mDatestampFormatter.format( - new Date( System.currentTimeMillis() ) ) ); - sb.append( " LTR " ); - sb.append( mMessageDirection.name() ); - sb.append( " [" ); - sb.append( mCRC.getAbbreviation() ); - sb.append( "] " ); - sb.append( getMessage() ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); + return mCRC.passes(); } + /** + * Raw binary message + */ + public CorrectedBinaryMessage getMessage() + { + return mMessage; + } + + /** + * CRC error check stats + */ public CRC getCRC() { - return mCRC; - } - - public int getArea() - { - return mMessage.getInt( AREA ); - } - - public int getChannel() - { - return mMessage.getInt( CHANNEL ); - } - - public String getChannelFormatted() - { - return StringUtils.leftPad( String.valueOf( getChannel() ), 2, "0" ); - } - - public int getHomeRepeater() - { - return mMessage.getInt( HOME_REPEATER ); - } - - public String getHomeRepeaterFormatted() - { - return StringUtils.leftPad( String.valueOf( getHomeRepeater() ), 2, "0" ); - } - - public int getGroup() - { - return mMessage.getInt( GROUP ); - } - - public String getGroupFormatted() - { - return StringUtils.leftPad( String.valueOf( getGroup() ), 3, "0" ); - } - - public int getFree() - { - return mMessage.getInt( FREE ); - } - - public String getFreeFormatted() - { - return StringUtils.leftPad( String.valueOf( getFree() ), 2, "0" ); - } - - @Override - public String getFromID() - { - return null; + return mCRC; } - @Override - public Alias getFromIDAlias() + /** + * Area: 0 or 1 + */ + public int getArea() { - return null; + return mMessage.getInt(AREA); } - @Override - public String getToID() + /** + * Logical Channel Number (LCN) for the repeater + */ + public int getChannel() { - StringBuilder sb = new StringBuilder(); - - sb.append( getArea() ); - sb.append( "-" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( "-" ); - sb.append( getGroupFormatted() ); - - return sb.toString(); + return mMessage.getInt(CHANNEL); } - @Override - public Alias getToIDAlias() + /** + * Home repeater number for the talkgroup + */ + public int getHomeRepeater() { - return mAliasList.getTalkgroupAlias( getToID() ); + return mMessage.getInt(HOME_REPEATER); } - public List getAliases() - { - List aliases = new ArrayList<>(); - - Alias talkgroupAlias = getToIDAlias(); - - if( talkgroupAlias != null ) - { - aliases.add( talkgroupAlias ); - } - - return aliases; - } - - public int getCRCChecksum() - { - return mMessage.getInt( CHECKSUM ); - } - - @Override - public String getProtocol() + /** + * Talkgroup number + */ + public int getGroup() { - return DecoderType.LTR_STANDARD.getDisplayString(); + return mMessage.getInt(GROUP); } - @Override - public String getEventType() + /** + * Free or available repeater channel for other subscribers to use when this repeater channel is busy + */ + public int getFree() { - return getMessageType().getDisplayText(); + return mMessage.getInt(FREE); } - @Override - public String getErrorStatus() + /** + * Talkgroup identifier + */ + public LTRTalkgroup getTalkgroup() { - return mCRC.getDisplayText(); + if(mTalkgroup == null) + { + mTalkgroup = LTRTalkgroup.create((getHomeRepeater() << 8) + getGroup()); + } + + return mTalkgroup; + } + + @Override + public Protocol getProtocol() + { + return Protocol.LTR_STANDARD; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java index 41f13ccf..17bf3d50 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java @@ -1,57 +1,64 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.ltrstandard.message; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.MessageDirection; -import io.github.dsheirer.message.MessageType; +import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType; +import java.util.Collections; +import java.util.List; + +/** + * Unknown message type + */ public class UnknownMessage extends LTRStandardMessage { - public UnknownMessage(BinaryMessage message, MessageDirection direction, AliasList list, CRC crc ) + public UnknownMessage(CorrectedBinaryMessage message, MessageDirection direction, CRC crc) { - super( message, direction, list, crc ); + super(message, direction, crc); } - @Override - public MessageType getMessageType() - { - return MessageType.UN_KNWN; - } - - @Override - public String getMessage() + @Override + public LtrStandardMessageType getMessageType() { - StringBuilder sb = new StringBuilder(); + return LtrStandardMessageType.UNKNOWN; + } - sb.append( "UNKNOWN AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " HOME:" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( " GRP:" ); - sb.append( getGroupFormatted() ); - sb.append( " FREE:" ); - sb.append( getFreeFormatted() ); + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("UNKNOWN AREA:").append(getArea()); + sb.append(" LCN:").append(getChannel()); + sb.append(" HOME:").append(getHomeRepeater()); + sb.append(" GRP:").append(getGroup()); + sb.append(" FREE:").append(getFree()); + return sb.toString(); + } - return sb.toString(); + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCCallEvent.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCCallEvent.java deleted file mode 100644 index 05933a40..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCCallEvent.java +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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.mdc1200; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class MDCCallEvent extends CallEvent -{ - public MDCCallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String details ) - { - super( DecoderType.MDC1200, callEventType, - aliasList, fromID, toID, details ); - } - - private Alias getAlias( String ident ) - { - if( hasAliasList() ) - { - return getAliasList().getMDC1200Alias( ident ); - } - - return null; - } - - @Override - public Alias getFromIDAlias() - { - return getAlias( getFromID() ); - } - - @Override - public Alias getToIDAlias() - { - return getAlias( getToID() ); - } - - @Override - public String getChannel() - { - return null; - } - - @Override - public long getFrequency() - { - return 0; - } - - public static class Builder - { - /* Required parameters */ - private CallEventType mCallEventType; - - /* Optional parameters */ - private AliasList mAliasList; - private String mFromID; - private String mToID; - private String mDetails; - - public Builder( CallEventType callEventType ) - { - mCallEventType = callEventType; - } - - public Builder aliasList( AliasList aliasList ) - { - mAliasList = aliasList; - return this; - } - - public Builder from( String val ) - { - mFromID = val; - return this; - } - - public Builder details( String details ) - { - mDetails = details; - return this; - } - - public Builder to( String toID ) - { - mToID = toID; - return this; - } - - public MDCCallEvent build() - { - return new MDCCallEvent( this ); - } - } - - /** - * Private constructor for the builder - */ - private MDCCallEvent( Builder builder ) - { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mDetails ); - } - - public static MDCCallEvent - getMDCCallEvent( MDCMessage message ) - { - CallEventType type = CallEventType.UNKNOWN; - StringBuilder sbDetails = new StringBuilder(); - sbDetails.append( "MDC-1200 OPCODE:" ); - sbDetails.append( String.valueOf( message.getOpcode() ) ); - - switch( message.getMessageType() ) - { - case ACKNOWLEDGE: - type = CallEventType.RESPONSE; - break; - case ANI: - type = CallEventType.ID_ANI; - break; - case EMERGENCY: - type = CallEventType.EMERGENCY; - break; - case PAGING: - type = CallEventType.PAGE; - break; - default: - break; - } - - return new MDCCallEvent.Builder( type ) - .details( sbDetails.toString() ) - .from( message.getFromID() ) - .to( message.getToID() ) - .build(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java index f4910c4a..31c8eb66 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.mdc1200; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.bits.SyncPattern; import io.github.dsheirer.dsp.NRZDecoder; @@ -34,25 +37,25 @@ public class MDCDecoder extends AbstractAFSKDecoder private MessageFramer mMessageFramer; private MDCMessageProcessor mMessageProcessor; - public MDCDecoder(AliasList aliasList) + public MDCDecoder() { super(AFSK1200Decoder.Output.INVERTED); - init(aliasList); + init(); } - protected MDCDecoder(AFSK1200Decoder decoder, AliasList aliasList) + protected MDCDecoder(AFSK1200Decoder decoder) { super(decoder); - init(aliasList); + init(); } - private void init(AliasList aliasList) + private void init() { mNRZDecoder = new NRZDecoder(NRZDecoder.MODE_INVERTED); getDecoder().setSymbolProcessor(mNRZDecoder); mMessageFramer = new MessageFramer(SyncPattern.MDC1200.getPattern(), MESSAGE_LENGTH); mNRZDecoder.setListener(mMessageFramer); - mMessageProcessor = new MDCMessageProcessor(aliasList); + mMessageProcessor = new MDCMessageProcessor(); mMessageFramer.addMessageListener(mMessageProcessor); mMessageProcessor.addMessageListener(getMessageListener()); } diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderInstrumented.java index 526c5c3f..422c1745 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderInstrumented.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.mdc1200; import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented; @@ -29,7 +33,7 @@ public class MDCDecoderInstrumented extends MDCDecoder implements IInstrumentedA public MDCDecoderInstrumented() { - super(new AFSK1200DecoderInstrumented(false), null); + super(new AFSK1200DecoderInstrumented(false)); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java index 8f8667fb..6997e0e9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,42 +15,32 @@ * * 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.mdc1200; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.util.StringUtils; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier; import java.util.Iterator; +import java.util.Set; import java.util.TreeSet; public class MDCDecoderState extends DecoderState { - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); + private Set mIdents = new TreeSet<>(); + private Set mEmergencyIdents = new TreeSet<>(); - private AliasedStringAttributeMonitor mFromAttribute; - private String mMessage; - private String mMessageType; - - public MDCDecoderState(AliasList aliasList) + public MDCDecoderState() { - super(aliasList); - - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MDC1200); } @Override @@ -85,37 +76,26 @@ public class MDCDecoderState extends DecoderState } - private void resetState() - { - mFromAttribute.reset(); - mMessage = null; - mMessageType = null; - } - @Override - public void receive(Message message) + public void receive(IMessage message) { if(message instanceof MDCMessage) { - MDCMessage mdc = (MDCMessage) message; + MDCMessage mdc = (MDCMessage)message; - mIdents.add(mdc.getUnitID()); + mIdents.add(mdc.getFromIdentifier()); if(mdc.isEmergency()) { - mEmergencyIdents.add(mdc.getUnitID()); + mEmergencyIdents.add(mdc.getFromIdentifier()); } - mFromAttribute.process(mdc.getFromID()); - MDCMessageType type = mdc.getMessageType(); - setMessageType(type.getLabel()); - StringBuilder sb = new StringBuilder(); sb.append("OPCODE "); - sb.append(String.valueOf(mdc.getOpcode())); + sb.append(mdc.getOpcode()); if(mdc.isBOT()) { @@ -127,11 +107,15 @@ public class MDCDecoderState extends DecoderState sb.append(" TYPE:EOT"); } - setMessage(sb.toString()); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(message.getIdentifiers()); - MDCCallEvent event = MDCCallEvent.getMDCCallEvent(mdc); - event.setAliasList(getAliasList()); - broadcast(event); + broadcast(DecodeEvent.builder(mdc.getTimestamp()) + .eventDescription(type.getLabel()) + .details(mdc.toString()) + .identifiers(ic) + .build()); switch(type) { @@ -150,34 +134,6 @@ public class MDCDecoderState extends DecoderState } } - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, mMessage)); - } - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType(String type) - { - if(!StringUtils.isEqual(mMessageType, type)) - { - mMessageType = type; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, "MDC:" + mMessageType)); - } - } - @Override public String getActivitySummary() { @@ -193,27 +149,11 @@ public class MDCDecoderState extends DecoderState } else { - Iterator it = mIdents.iterator(); + Iterator it = mIdents.iterator(); while(it.hasNext()) { - String ident = it.next(); - - sb.append(" "); - sb.append(ident); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMDC1200Alias(ident); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); + sb.append(" ").append(it.next()).append("\n"); } } @@ -225,27 +165,16 @@ public class MDCDecoderState extends DecoderState } else { - Iterator it = mEmergencyIdents.iterator(); + Iterator it = mEmergencyIdents.iterator(); while(it.hasNext()) { - String ident = it.next(); + Iterator it2 = mIdents.iterator(); - sb.append(" "); - sb.append(ident); - sb.append(" "); - - if(hasAliasList()) + while(it2.hasNext()) { - Alias alias = getAliasList().getMDC1200Alias(ident); - - if(alias != null) - { - sb.append(alias.getName()); - } + sb.append(" ").append(it2.next()).append("\n"); } - - sb.append("\n"); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessage.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessage.java index f73027df..1dfcaa88 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessage.java @@ -1,345 +1,261 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.mdc1200; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier; +import io.github.dsheirer.protocol.Protocol; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.List; public class MDCMessage extends Message { - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); + private static int[] SYNC1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; - private static int[] sSYNC1 = { 0,1,2,3,4,5,6,7,8,9, - 10,11,12,13,14,15,16,17,18,19, - 20,21,22,23,24,25,26,27,28,29, - 30,31,32,33,34,35,36,37,38,39 }; - - private static int[] sOPCODE = { 47,46,45,44,43,42,41,40 }; - private static int sANI_FLAG = 40; - private static int sDIRECTION_FLAG = 45; - private static int sACKNOWLEDGE_REQUIRED_FLAG = 46; - private static int sPACKET_TYPE_FLAG = 47; - private static int sEMERGENCY_FLAG = 48; - private static int[] sARGUMENT = { 49,50,51,52,53,54 }; - private static int sBOT_EOT_FLAG = 55; - private static int[] sDIGIT_2 = { 59,58,57,56 }; - private static int[] sDIGIT_1 = { 63,62,61,60 }; - private static int[] sDIGIT_4 = { 67,66,65,64 }; - private static int[] sDIGIT_3 = { 71,70,69,68 }; + private static int[] OPCODE = {47, 46, 45, 44, 43, 42, 41, 40}; + private static int ANI_FLAG = 40; + private static int DIRECTION_FLAG = 45; + private static int ACKNOWLEDGE_REQUIRED_FLAG = 46; + private static int PACKET_TYPE_FLAG = 47; + private static int EMERGENCY_FLAG = 48; + private static int[] ARGUMENT = {49, 50, 51, 52, 53, 54}; + private static int BOT_EOT_FLAG = 55; + private static int[] DIGIT_2 = {59, 58, 57, 56}; + private static int[] DIGIT_1 = {63, 62, 61, 60}; + private static int[] DIGIT_4 = {67, 66, 65, 64}; + private static int[] DIGIT_3 = {71, 70, 69, 68}; + private static int[] IDENTITY = {63, 62, 61, 60, 59, 58, 57, 56, 71, 70, 69, 68, 67, 66, 65, 64}; - private BinaryMessage mMessage; - private AliasList mAliasList; - - public MDCMessage( BinaryMessage message, AliasList list ) + private CorrectedBinaryMessage mMessage; + private MDC1200Identifier mFromIdentifier; + private List mIdentifiers; + + public MDCMessage(CorrectedBinaryMessage message) { mMessage = message; - mAliasList = list; } - + + public CorrectedBinaryMessage getMessage() + { + return mMessage; + } + + public MDC1200Identifier getFromIdentifier() + { + if(mFromIdentifier == null) + { + mFromIdentifier = MDC1200Identifier.createFrom(getMessage().getInt(IDENTITY)); + } + + return mFromIdentifier; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getFromIdentifier()); + } + + return mIdentifiers; + } + public boolean isValid() { - //TODO: add CRC and/or convolution decoding/repair + //TODO: add CRC and/or convolution decoding/repair return true; } public PacketType getPacketType() { - if( mMessage.get( sPACKET_TYPE_FLAG ) ) - { - return PacketType.DATA; - } - else - { - return PacketType.CMND; - } + if(mMessage.get(PACKET_TYPE_FLAG)) + { + return PacketType.DATA; + } + else + { + return PacketType.CMND; + } } - + public Acknowledge getResponse() { - if( mMessage.get( sACKNOWLEDGE_REQUIRED_FLAG ) ) - { - return Acknowledge.YES; - } - else - { - return Acknowledge.NO; - } + if(mMessage.get(ACKNOWLEDGE_REQUIRED_FLAG)) + { + return Acknowledge.YES; + } + else + { + return Acknowledge.NO; + } } - + public Direction getDirection() { - if( mMessage.get( sDIRECTION_FLAG ) ) - { - return Direction.OUT; - } - else - { - return Direction.IN; - } + if(mMessage.get(DIRECTION_FLAG)) + { + return Direction.OUT; + } + else + { + return Direction.IN; + } } - + public int getOpcode() { - return mMessage.getInt( sOPCODE ); + return mMessage.getInt(OPCODE); } - + public int getArgument() { - return mMessage.getInt( sARGUMENT ); + return mMessage.getInt(ARGUMENT); } - + public boolean isEmergency() { - return mMessage.get( sEMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - - + + public boolean isANI() { - return mMessage.get( sANI_FLAG ); + return mMessage.get(ANI_FLAG); } - + public boolean isBOT() { - return mMessage.get( sBOT_EOT_FLAG ); + return mMessage.get(BOT_EOT_FLAG); } - + public boolean isEOT() { - return !mMessage.get( sBOT_EOT_FLAG ); + return !mMessage.get(BOT_EOT_FLAG); } - - public String getUnitID() - { - StringBuilder sb = new StringBuilder(); - sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_1 ) ).toUpperCase() ); - sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_2 ) ).toUpperCase() ); - sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_3 ) ).toUpperCase() ); - sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_4 ) ).toUpperCase() ); - - return sb.toString(); - } - public MDCMessageType getMessageType() { - switch( getOpcode() ) - { - case 0: - if( isEmergency() ) - { - return MDCMessageType.EMERGENCY; - } - case 1: - return MDCMessageType.ANI; - default: - return MDCMessageType.UNKNOWN; - } - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) ); - - sb.append( getMessage() ); - - sb.append( " [" + mMessage.toString() + "]" ); - - return sb.toString(); + switch(getOpcode()) + { + case 0: + if(isEmergency()) + { + return MDCMessageType.EMERGENCY; + } + case 1: + return MDCMessageType.ANI; + default: + return MDCMessageType.UNKNOWN; + } } /** * Pads spaces onto the end of the value to make it 'places' long */ - public String pad( String value, int places, String padCharacter ) + public String pad(String value, int places, String padCharacter) { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(value); + + while(sb.length() < places) + { + sb.append(padCharacter); + } + + return sb.toString(); } - + /** * Pads an integer value with additional zeroes to make it decimalPlaces long */ - public String format( int number, int decimalPlaces ) + public String format(int number, int decimalPlaces) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); + int paddingRequired = decimalPlaces - (String.valueOf(number).length()); - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - @SuppressWarnings( "unused" ) - private int getInt( int[] bits ) - { - int retVal = 0; - - for( int x = 0; x < bits.length; x++ ) - { - if( mMessage.get( bits[ x ] ) ) - { - retVal += 1< getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - return aliases; - } - - private enum PacketType { CMND, DATA }; - private enum Acknowledge { YES, NO }; - private enum Direction { IN, OUT }; } diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java index 1cae450d..8a49cb46 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java @@ -2,55 +2,56 @@ package io.github.dsheirer.module.decode.mdc1200; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.Message; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; -public class MDCMessageFilter extends Filter +public class MDCMessageFilter extends Filter { - private HashMap> mElements = - new HashMap>(); - - public MDCMessageFilter() - { - super( "MDC-1200 Message Filter" ); + private Map> mElements = new HashMap>(); - for( MDCMessageType type: MDCMessageType.values() ) - { - if( type != MDCMessageType.UNKNOWN ) - { - mElements.put( type, new FilterElement( type ) ); - } - } - } - - @Override - public boolean passes( Message message ) + public MDCMessageFilter() { - if( mEnabled && canProcess( message ) ) - { - MDCMessage mdc = (MDCMessage)message; - - if( mElements.containsKey( mdc.getMessageType() ) ) - { - return mElements.get( mdc.getMessageType() ).isEnabled(); - } - } - - return false; + super("MDC-1200 Message Filter"); + + for(MDCMessageType type : MDCMessageType.values()) + { + if(type != MDCMessageType.UNKNOWN) + { + mElements.put(type, new FilterElement(type)); + } + } } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof MDCMessage; + if(mEnabled && canProcess(message)) + { + MDCMessage mdc = (MDCMessage) message; + + if(mElements.containsKey(mdc.getMessageType())) + { + return mElements.get(mdc.getMessageType()).isEnabled(); + } + } + + return false; } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof MDCMessage; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mElements.values() ); + return new ArrayList>(mElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageProcessor.java index b4511e93..0265e971 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageProcessor.java @@ -1,155 +1,155 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.mdc1200; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; -public class MDCMessageProcessor implements Listener +public class MDCMessageProcessor implements Listener { - private static int sMESSAGE_LENGTH = 112; - - private Broadcaster mBroadcaster = new Broadcaster(); - private AliasList mAliasList; - - public MDCMessageProcessor( AliasList aliasList ) - { - mAliasList = aliasList; - } - - public void dispose() - { - mBroadcaster.dispose(); - } - - @Override - public void receive( BinaryMessage buffer ) - { - /** - * De-interleave the 112 bits of message one, starting after the sync - * pattern - */ - deinterleave( buffer, 40 ); + private static int sMESSAGE_LENGTH = 112; - /** - * For simplicity, we also deinterleave message 2, which may or may not - * be there, starting after the initial sync ( 0 - 39 ) and the initial - * message ( 40 - 151 ) and the second sync ( 152 - 191 ). - */ - deinterleave( buffer, 192 ); + private Broadcaster mBroadcaster = new Broadcaster<>(); - /** - * Wrap the buffer in a message along with the designated alias list - * and send it on its merry way - */ - MDCMessage message = new MDCMessage( buffer, mAliasList ); - - mBroadcaster.receive( message ); - } - - public void addMessageListener( Listener listener ) + public MDCMessageProcessor() { - mBroadcaster.addListener( listener ); } - public void removeMessageListener( Listener listener ) + public void dispose() { - mBroadcaster.removeListener( listener ); + mBroadcaster.dispose(); } - - /** - * Removes NRZ-I encoding from the entire buffer. Assumes that the initial - * bit will be a 0 after decode, despite what is currently in the buffer, - * in order to maintain correct polarity of the decoded message. - */ - private void removeNRZIEncoding( BinaryMessage buffer ) - { - /** - * Clear bit position 0, the first bit of the sync pattern, to start - * the decode with the correct polarity - */ - buffer.clear( 0 ); - /** - * Set each bit according to the state of the previous bit using XOR - * as follows: - * - * 0 = same as previous state - * 1 = inverse of previous state - */ - for( int x = 1; x < buffer.size(); x++ ) - { - if( buffer.get( x - 1 ) ^ buffer.get( x ) ) - { - buffer.set( x ); - } - else - { - buffer.clear( x ); - } - } - } - - /** - * Deinterleaves a 112-bit packet, starting at the offset into the buffer - */ - private void deinterleave( BinaryMessage buffer, int offset ) - { - if( buffer.size() < sMESSAGE_LENGTH + offset ) - { - throw new IllegalArgumentException( "MDCMessageProcessor - " - + "cannot deinterleave message - message buffer too short" ); - } - - BinaryMessage deinterleaved = new BinaryMessage( 112 ); + @Override + public void receive(CorrectedBinaryMessage buffer) + { + /** + * De-interleave the 112 bits of message one, starting after the sync + * pattern + */ + deinterleave(buffer, 40); - int deinterleavedPointer = 0; - - for( int column = 0; column < 16; column++ ) - { - for( int row = 0; row < 7; row++ ) - { - if( buffer.get( ( row * 16 ) + column + offset ) ) - { - deinterleaved.set( deinterleavedPointer ); - } - - deinterleavedPointer++; - } - } + /** + * For simplicity, we also deinterleave message 2, which may or may not + * be there, starting after the initial sync ( 0 - 39 ) and the initial + * message ( 40 - 151 ) and the second sync ( 152 - 191 ). + */ + deinterleave(buffer, 192); - //Overlay the deinterleaved bits back onto the source message and - //return it - buffer.clear( offset, offset + sMESSAGE_LENGTH ); - - for( int x = 0; x < sMESSAGE_LENGTH; x++ ) - { - if( deinterleaved.get( x ) ) - { - buffer.set( x + offset ); - } - else - { - buffer.clear( x + offset ); - } - } - } + /** + * Wrap the buffer in a message along with the designated alias list + * and send it on its merry way + */ + MDCMessage message = new MDCMessage(buffer); + + mBroadcaster.receive(message); + } + + public void addMessageListener(Listener listener) + { + mBroadcaster.addListener(listener); + } + + public void removeMessageListener(Listener listener) + { + mBroadcaster.removeListener(listener); + } + + /** + * Removes NRZ-I encoding from the entire buffer. Assumes that the initial + * bit will be a 0 after decode, despite what is currently in the buffer, + * in order to maintain correct polarity of the decoded message. + */ + private void removeNRZIEncoding(BinaryMessage buffer) + { + /** + * Clear bit position 0, the first bit of the sync pattern, to start + * the decode with the correct polarity + */ + buffer.clear(0); + + /** + * Set each bit according to the state of the previous bit using XOR + * as follows: + * + * 0 = same as previous state + * 1 = inverse of previous state + */ + for(int x = 1; x < buffer.size(); x++) + { + if(buffer.get(x - 1) ^ buffer.get(x)) + { + buffer.set(x); + } + else + { + buffer.clear(x); + } + } + } + + /** + * Deinterleaves a 112-bit packet, starting at the offset into the buffer + */ + private void deinterleave(BinaryMessage buffer, int offset) + { + if(buffer.size() < sMESSAGE_LENGTH + offset) + { + throw new IllegalArgumentException("MDCMessageProcessor - " + + "cannot deinterleave message - message buffer too short"); + } + + BinaryMessage deinterleaved = new BinaryMessage(112); + + int deinterleavedPointer = 0; + + for(int column = 0; column < 16; column++) + { + for(int row = 0; row < 7; row++) + { + if(buffer.get((row * 16) + column + offset)) + { + deinterleaved.set(deinterleavedPointer); + } + + deinterleavedPointer++; + } + } + + //Overlay the deinterleaved bits back onto the source message and + //return it + buffer.clear(offset, offset + sMESSAGE_LENGTH); + + for(int x = 0; x < sMESSAGE_LENGTH; x++) + { + if(deinterleaved.get(x)) + { + buffer.set(x + offset); + } + else + { + buffer.clear(x + offset); + } + } + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/identifier/MDC1200Identifier.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/identifier/MDC1200Identifier.java new file mode 100644 index 00000000..1914bd09 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/identifier/MDC1200Identifier.java @@ -0,0 +1,62 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.mdc1200.identifier; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Identifier for an MDC1200 radio identity + */ +public class MDC1200Identifier extends TalkgroupIdentifier +{ + + /** + * Constructs an MDC1200 talkgroup Identifier + */ + public MDC1200Identifier(Integer value, Role role) + { + super(value, role, false); + } + + @Override + public Protocol getProtocol() + { + return Protocol.MDC1200; + } + + /** + * Creates an MDC1200 talkgroup identifier for the TO identity + */ + public static MDC1200Identifier createTo(int value) + { + return new MDC1200Identifier(value, Role.TO); + } + + /** + * Creates an MDC1200 talkgroup identifier for the FROM identity + */ + public static MDC1200Identifier createFrom(int value) + { + return new MDC1200Identifier(value, Role.FROM); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327CallEvent.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327CallEvent.java deleted file mode 100644 index 776a6d8c..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327CallEvent.java +++ /dev/null @@ -1,154 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.mpt1327; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class MPT1327CallEvent extends CallEvent -{ - private String mChannel; - private long mFrequency; - - public MPT1327CallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String channel, - long frequency, - String details ) - { - super( DecoderType.MPT1327, callEventType, - aliasList, fromID, toID, details ); - - mChannel = channel; - mFrequency = frequency; - } - - private Alias getAlias(String mpt1327Talkgroup ) - { - if( hasAliasList() ) - { - return getAliasList().getMPT1327Alias( mpt1327Talkgroup ); - } - - return null; - } - - @Override - public Alias getFromIDAlias() - { - return getAlias( getFromID() ); - } - - @Override - public Alias getToIDAlias() - { - return getAlias( getToID() ); - } - - @Override - public String getChannel() - { - return mChannel; - } - - @Override - public long getFrequency() - { - return mFrequency; - } - - public static class Builder - { - /* Required parameters */ - private CallEventType mCallEventType; - - /* Optional parameters */ - private AliasList mAliasList; - private String mFromID; - private String mToID; - private String mDetails; - private String mChannel; - private long mFrequency; - - public Builder( CallEventType callEventType ) - { - mCallEventType = callEventType; - } - - public Builder aliasList( AliasList aliasList ) - { - mAliasList = aliasList; - return this; - } - - public Builder from( String val ) - { - mFromID = val; - return this; - } - - public Builder channel( String channel ) - { - mChannel = channel; - return this; - } - - public Builder details( String details ) - { - mDetails = details; - return this; - } - - public Builder frequency( long frequency ) - { - mFrequency = frequency; - return this; - } - - public Builder to( String toID ) - { - mToID = toID; - return this; - } - - public MPT1327CallEvent build() - { - return new MPT1327CallEvent( this ); - } - - } - - /** - * Private constructor for the builder - */ - private MPT1327CallEvent( Builder builder ) - { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mChannel, - builder.mFrequency, - builder.mDetails ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java new file mode 100644 index 00000000..18506fcc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java @@ -0,0 +1,142 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.mpt1327; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.module.decode.p25.P25DecodeEvent; +import io.github.dsheirer.protocol.Protocol; + +public class MPT1327ChannelGrantEvent extends P25DecodeEvent +{ + public MPT1327ChannelGrantEvent(long timestamp) + { + super(timestamp); + } + + /** + * Creates a new decode event builder with the specified start timestamp. + * @param timeStart for the event + * @return builder + */ + public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(long timeStart) + { + return new MPT1327ChannelGrantDecodeEventBuilder(timeStart); + } + + /** + * Builder pattern for constructing decode events. + */ + public static class MPT1327ChannelGrantDecodeEventBuilder + { + protected long mTimeStart; + protected long mDuration; + protected String mEventDescription; + protected IdentifierCollection mIdentifierCollection; + protected IChannelDescriptor mChannelDescriptor; + protected String mDetails; + + /** + * Constructs a builder instance with the specified start time in milliseconds + * + * @param timeStart + */ + public MPT1327ChannelGrantDecodeEventBuilder(long timeStart) + { + mTimeStart = timeStart; + } + + /** + * Sets the duration value + * @param duration in milliseconds + */ + public MPT1327ChannelGrantDecodeEventBuilder duration(long duration) + { + mDuration = duration; + return this; + } + + /** + * Sets the duration value using the end - start timestamps + * @param timestamp for end of event + */ + public MPT1327ChannelGrantDecodeEventBuilder end(long timestamp) + { + mDuration = timestamp - mTimeStart; + return this; + } + + /** + * Sets the channel descriptor for this event + * @param channelDescriptor + */ + public MPT1327ChannelGrantDecodeEventBuilder channel(IChannelDescriptor channelDescriptor) + { + mChannelDescriptor = channelDescriptor; + return this; + } + + /** + * Sets the event description text + * @param description of the event + */ + public MPT1327ChannelGrantDecodeEventBuilder eventDescription(String description) + { + mEventDescription = description; + return this; + } + + /** + * Sets the identifier collection. + * @param identifierCollection containing optional identifiers like TO, FROM, frequency and + * alias list configuration name. + */ + public MPT1327ChannelGrantDecodeEventBuilder identifiers(IdentifierCollection identifierCollection) + { + mIdentifierCollection = identifierCollection; + return this; + } + + /** + * Sets the details for the event + * @param details + */ + public MPT1327ChannelGrantDecodeEventBuilder details(String details) + { + mDetails = details; + return this; + } + + /** + * Builds the decode event + */ + public MPT1327ChannelGrantEvent build() + { + MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mTimeStart); + decodeEvent.setProtocol(Protocol.MPT1327); + decodeEvent.setChannelDescriptor(mChannelDescriptor); + decodeEvent.setDetails(mDetails); + decodeEvent.setDuration(mDuration); + decodeEvent.setEventDescription(mEventDescription); + decodeEvent.setIdentifierCollection(mIdentifierCollection); + return decodeEvent; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java index 2c0ae2a2..b835f180 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java @@ -1,33 +1,40 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.mpt1327; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.IBinarySymbolProcessor; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.dsp.afsk.AFSK1200Decoder; +import io.github.dsheirer.dsp.symbol.BinaryToByteBufferAssembler; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.afsk.AbstractAFSKDecoder; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider; +import io.github.dsheirer.sample.buffer.ReusableByteBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Instrumented version of the MPT1327 decoder. Exposes properties for instrumented manual decoding of a signal. */ -public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbolProcessor +public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbolProcessor, IReusableByteBufferProvider { private final static Logger mLog = LoggerFactory.getLogger(MPT1327Decoder.class); @@ -38,20 +45,21 @@ public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbol private MessageFramer mControlMessageFramer; private MessageFramer mTrafficMessageFramer; private MPT1327MessageProcessor mMessageProcessor; + private BinaryToByteBufferAssembler mBinaryToByteBufferAssembler = new BinaryToByteBufferAssembler(512); - protected MPT1327Decoder(AFSK1200Decoder decoder, AliasList aliasList, Sync sync) + protected MPT1327Decoder(AFSK1200Decoder decoder, Sync sync) { super(decoder); - init(aliasList, sync); + init(sync); } - public MPT1327Decoder(AliasList aliasList, Sync sync) + public MPT1327Decoder(Sync sync) { super((sync == Sync.NORMAL ? AFSK1200Decoder.Output.NORMAL : AFSK1200Decoder.Output.INVERTED)); - init(aliasList, sync); + init(sync); } - private void init(AliasList aliasList, Sync sync) + private void init(Sync sync) { getDecoder().setSymbolProcessor(this); @@ -62,17 +70,18 @@ public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbol mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), MESSAGE_LENGTH); //Fully decoded and framed messages processor - mMessageProcessor = new MPT1327MessageProcessor(aliasList); + mMessageProcessor = new MPT1327MessageProcessor(); mMessageProcessor.setMessageListener(getMessageListener()); mControlMessageFramer.addMessageListener(mMessageProcessor); mTrafficMessageFramer.addMessageListener(mMessageProcessor); } - public void receive(boolean symbol) + public void process(boolean symbol) { - mControlMessageFramer.receive(symbol); - mTrafficMessageFramer.receive(symbol); + mControlMessageFramer.process(symbol); + mTrafficMessageFramer.process(symbol); + mBinaryToByteBufferAssembler.process(symbol); } @Override @@ -84,6 +93,12 @@ public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbol @Override public void reset() { + } + + @Override + public void start() + { + super.start(); mControlMessageFramer.reset(); mTrafficMessageFramer.reset(); } @@ -109,4 +124,22 @@ public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbol { return mTrafficMessageFramer; } + + @Override + public void setBufferListener(Listener listener) + { + mBinaryToByteBufferAssembler.setBufferListener(listener); + } + + @Override + public void removeBufferListener(Listener listener) + { + mBinaryToByteBufferAssembler.removeBufferListener(listener); + } + + @Override + public boolean hasBufferListeners() + { + return mBinaryToByteBufferAssembler.hasBufferListeners(); + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderInstrumented.java index eb6d31ac..fa76b186 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderInstrumented.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.mpt1327; import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented; @@ -29,7 +33,7 @@ public class MPT1327DecoderInstrumented extends MPT1327Decoder implements IInstr public MPT1327DecoderInstrumented() { - super(new AFSK1200DecoderInstrumented(true), null, Sync.FRENCH); + super(new AFSK1200DecoderInstrumented(true), Sync.NORMAL); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java index d09cf40d..7cd67fd3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,28 +15,24 @@ * * 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.mpt1327; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.ChangeChannelTimeoutEvent; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.channel.traffic.TrafficChannelAllocationEvent; import io.github.dsheirer.controller.channel.Channel.ChannelType; -import io.github.dsheirer.controller.channel.map.ChannelMap; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.config.DecodeConfiguration; -import io.github.dsheirer.module.decode.event.CallEvent; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel; +import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,27 +52,31 @@ public class MPT1327DecoderState extends DecoderState private HashMap> mGroups = new HashMap>(); private String mSite; - private AliasedStringAttributeMonitor mFromAttribute; - private AliasedStringAttributeMonitor mToAttribute; private int mChannelNumber; private ChannelType mChannelType; - private ChannelMap mChannelMap; private long mFrequency = 0; private long mCallTimeout; + private MPT1327TrafficChannelManager mMPT1327TrafficChannelManager; - public MPT1327DecoderState(AliasList aliasList, ChannelMap channelMap, ChannelType channelType, long callTimeout) + /** + * Constructs an MPT-1327 decoder state with an optional traffic channel manager for allocating traffic channels + * when go to channel (GTC) messages are detected. + */ + public MPT1327DecoderState(MPT1327TrafficChannelManager trafficChannelManager, ChannelType channelType, long callTimeout) { - super(aliasList); - - mChannelMap = channelMap; + mMPT1327TrafficChannelManager = trafficChannelManager; mChannelType = channelType; mCallTimeout = callTimeout; - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MPT1327); - mToAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MPT1327); + } + + /** + * Constructs an MPT1327 decoder state that does not allocate traffic channels + */ + public MPT1327DecoderState(ChannelType channelType, long callTimeout) + { + this(null, channelType, callTimeout); } @Override @@ -95,13 +96,13 @@ public class MPT1327DecoderState extends DecoderState } @Override - public void receive(Message message) + public void receive(IMessage message) { if(message.isValid()) { if(message instanceof MPT1327Message) { - MPT1327Message mpt = ((MPT1327Message) message); + MPT1327Message mpt = ((MPT1327Message)message); switch(mpt.getMessageType()) { @@ -112,166 +113,151 @@ public class MPT1327DecoderState extends DecoderState if(identType == MPT1327Message.IdentType.REGI) { - broadcast(new MPT1327CallEvent.Builder(CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details("REGISTERED ON NETWORK") - .frequency(mFrequency) - .from(mpt.getToID()) - .to(mpt.getFromID()) + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(mpt.getIdentifiers()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.MPT1327) + .eventDescription("Register") + .identifiers(ic) .build()); } else { - broadcast(new MPT1327CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(mpt.getIdentifiers()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.MPT1327) + .eventDescription("Response") .details("ACK " + identType.getLabel()) - .frequency(mFrequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) + .identifiers(ic) .build()); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); break; + case ACKB: + case ACKE: case ACKI: - mIdents.add(mpt.getFromID()); - mIdents.add(mpt.getToID()); + case ACKQ: + case ACKT: + case ACKV: + case ACKX: + MutableIdentifierCollection icACK = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icACK.remove(IdentifierClass.USER); + icACK.update(mpt.getIdentifiers()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.MPT1327) + .eventDescription("Acknowledge") + .details(mpt.getMessageType().getDescription()) + .identifiers(icACK) + .build()); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); break; case AHYC: - mIdents.add(mpt.getToID()); + MutableIdentifierCollection icAHY = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icAHY.remove(IdentifierClass.USER); + icAHY.update(mpt.getIdentifiers()); - broadcast(new MPT1327CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details(((MPT1327Message) message).getRequestString()) - .frequency(mFrequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.MPT1327) + .eventDescription("Command") + .details("Send Short Data Message") + .identifiers(icAHY) .build()); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); break; case AHYQ: - broadcast(new MPT1327CallEvent.Builder(CallEvent.CallEventType.STATUS) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details(mpt.getStatusMessage()) - .frequency(mFrequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) + MutableIdentifierCollection icAHYQ = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icAHYQ.remove(IdentifierClass.USER); + icAHYQ.update(mpt.getIdentifiers()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.MPT1327) + .eventDescription("Command") + .details("Send Status Message") + .identifiers(icAHYQ) .build()); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); break; case ALH: + case ALHD: + case ALHE: + case ALHF: + case ALHR: + case ALHS: + case ALHX: setSite(mpt.getSiteID()); broadcast(new DecoderStateEvent(this, Event.START, State.CONTROL)); break; case GTC: - String from = mpt.getFromID(); - String to = mpt.getToID(); - - if(from != null) + if(mMPT1327TrafficChannelManager != null) { - mIdents.add(from); + mMPT1327TrafficChannelManager.processChannelGrant(mpt); } - - if(to != null) + else { - mIdents.add(to); + MPT1327Channel channel = MPT1327Channel.create(mpt.getChannel()); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(mpt.getIdentifiers()); + + broadcast(DecodeEvent.builder(mpt.getTimestamp()) + .eventDescription("Call Detect") + .details(mpt.getMessage()) + .channel(channel) + .identifiers(ic) + .build()); } - - /* Capture the idents that talk to each group */ - if(from != null && to != null) - { - if(mGroups.containsKey(to)) - { - ArrayList talkgroups = mGroups.get(to); - - if(!talkgroups.contains(from)) - { - talkgroups.add(from); - } - } - else - { - ArrayList talkgroups = new ArrayList(); - talkgroups.add(from); - - mGroups.put(to, talkgroups); - } - } - - int channel = mpt.getChannel(); - - long frequency = 0; - - if(getChannelMap() != null) - { - frequency = getChannelMap().getFrequency(channel); - } - - CallEvent event = new MPT1327CallEvent.Builder(CallEvent.CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(channel)) - .details("GTC") - .frequency(frequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) - .build(); - - broadcast(new TrafficChannelAllocationEvent(this, event)); break; case HEAD_PLUS1: case HEAD_PLUS2: case HEAD_PLUS3: case HEAD_PLUS4: - broadcast(new MPT1327CallEvent.Builder(CallEvent.CallEventType.SDM) - .aliasList(getAliasList()) + MutableIdentifierCollection icHEAD = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icHEAD.remove(IdentifierClass.USER); + icHEAD.update(mpt.getIdentifiers()); + + broadcast(DecodeEvent.builder(message.getTimestamp()) + .protocol(Protocol.MPT1327) + .eventDescription("Short Data Message") .details(mpt.getMessage()) - .from(mpt.getFromID()) - .to(mpt.getToID()) + .identifiers(icHEAD) .build()); + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); break; - /* Traffic Channel Events */ + /* Traffic Channel Events */ case CLEAR: mChannelNumber = mpt.getChannel(); broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); break; case MAINT: + getIdentifierCollection().update(mpt.getToIdentifier()); + if(mChannelType == ChannelType.STANDARD) { - /** - * When we receive a MAINT message and we're configured - * as a standard channel, we need to apply the call - * timeout specified by the user. Otherwise we'll - * be using the shorter default call timeout - */ - broadcast(new ChangeChannelTimeoutEvent(this, - mChannelType, mCallTimeout)); + //When we receive a MAINT message and we're configured as a standard channel, apply the call + // timeout specified by the user. Otherwise we'll be using the shorter default call timeout + broadcast(new ChangeChannelTimeoutEvent(this, mChannelType, mCallTimeout)); - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new MPT1327CallEvent.Builder(CallEvent.CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details("MONITORED TRAFFIC CHANNEL") - .frequency(mFrequency) - .to(mpt.getToID()) - .build(); - - broadcast(mCurrentCallEvent); - } - - mToAttribute.process(mpt.getToID()); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(mpt.getIdentifiers()); + broadcast(DecodeEvent.builder(mpt.getTimestamp()) + .identifiers(ic) + .eventDescription("Call In Progress") + .build()); broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); } break; @@ -307,10 +293,9 @@ public class MPT1327DecoderState extends DecoderState } - private void resetState() + protected void resetState() { - mFromAttribute.reset(); - mToAttribute.reset(); + super.resetState(); /** * If this is a standard channel, reset the fade timeout to the default @@ -326,7 +311,6 @@ public class MPT1327DecoderState extends DecoderState if(mCurrentCallEvent != null) { mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); mCurrentCallEvent = null; } } @@ -345,31 +329,9 @@ public class MPT1327DecoderState extends DecoderState if(!StringUtils.isEqual(mSite, site)) { mSite = site; - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_1, "SITE:" + getSite(), - getSiteAlias())); } } - public Alias getSiteAlias() - { - if(hasAliasList()) - { - return getAliasList().getSiteID(mSite); - } - - return null; - } - - public ChannelMap getChannelMap() - { - return mChannelMap; - } - - public void setChannelMap(ChannelMap channelMap) - { - mChannelMap = channelMap; - } - public int getChannelNumber() { return mChannelNumber; @@ -385,7 +347,6 @@ public class MPT1327DecoderState extends DecoderState if(mChannelNumber != channel) { mChannelNumber = channel; - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, String.valueOf(mChannelNumber))); } } @@ -400,17 +361,8 @@ public class MPT1327DecoderState extends DecoderState if(mSite != null) { - sb.append(mSite); + sb.append(mSite).append("\n"); - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(mSite); - - if(siteAlias != null) - { - sb.append(" ").append(siteAlias.getName()).append("\n"); - } - } } else { @@ -432,17 +384,6 @@ public class MPT1327DecoderState extends DecoderState { sb.append("\n ").append(talkgroup); - if(hasAliasList()) - { - Alias alias = getAliasList().getMPT1327Alias(talkgroup); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - } - sb.append("\n"); ArrayList members = mGroups.get(talkgroup); @@ -453,17 +394,6 @@ public class MPT1327DecoderState extends DecoderState sb.append(" >"); sb.append(member); - if(hasAliasList()) - { - Alias alias = getAliasList().getMPT1327Alias(member); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - } - sb.append("\n"); } } @@ -485,20 +415,7 @@ public class MPT1327DecoderState extends DecoderState { String ident = it.next(); - sb.append(""); sb.append(ident); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMPT1327Alias(ident); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - sb.append("\n"); } } @@ -516,38 +433,6 @@ public class MPT1327DecoderState extends DecoderState break; case SOURCE_FREQUENCY: mFrequency = event.getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, event.getFrequency())); - break; - case TRAFFIC_CHANNEL_ALLOCATION: - if(event.getSource() != MPT1327DecoderState.this) - { - if(event instanceof TrafficChannelAllocationEvent) - { - TrafficChannelAllocationEvent allocationEvent = - (TrafficChannelAllocationEvent) event; - - String channel = allocationEvent.getCallEvent().getChannel(); - - if(channel != null) - { - try - { - setChannelNumber(Integer.valueOf(channel)); - } - catch(Exception e) - { - //Do nothing, we couldn't parse the channel number - } - } - - mFrequency = allocationEvent.getCallEvent().getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, - allocationEvent.getCallEvent().getFrequency())); - - mFromAttribute.process(allocationEvent.getCallEvent().getFromID()); - mToAttribute.process(allocationEvent.getCallEvent().getToID()); - } - } break; default: break; diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327IdentType.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327IdentType.java new file mode 100644 index 00000000..8d93313b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327IdentType.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.module.decode.mpt1327; + +/** + * MPT-1327 Ident Value Types + */ +public enum MPT1327IdentType +{ + +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java index 23cdf37b..6d3df6f9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java @@ -1,293 +1,277 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.mpt1327; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; import io.github.dsheirer.edac.CRC; import io.github.dsheirer.edac.CRCFleetsync; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.module.decode.mpt1327.identifier.MPT1327Talkgroup; +import io.github.dsheirer.protocol.Protocol; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; import java.util.List; public class MPT1327Message extends Message { - private static String[] TELEX_LETTERS = - { "A","B","C","D","E","F","G","H", - "I","J","K","L","M","N","O","P", - "Q","R","S","T","U","V","W","X", - "Y","Z","\n","\n","",""," "," " }; + private static String[] TELEX_LETTERS = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\n", "\n", "", "", " ", " "}; + private static String[] TELEX_FIGURES = {"-", "?", ":", "WRU", "3", "{6}", "{7}", "{8}", "8", "{BEEP}", "(", ")", ".", ",", "9", "0", "1", "4", "'", "5", "7", "=", "2", "/", "6", "+", "\n", "\n", "", "", " ", " "}; + private static String[] BCD = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " ", "*", "#"}; - private static String[] TELEX_FIGURES = - { "-","?",":","WRU","3","{6}","{7}","{8}", - "8","{BEEP}","(",")",".",",","9","0", - "1","4","'","5","7","=","2","/", - "6","+","\n","\n","",""," "," " }; - - private static String[] BCD = { "0","1","2","3","4","5","6", - "7","8","9"," ","*","#" }; - - private static int BLOCK_1_START = 20; - private static int BLOCK_2_START = 84; - private static int BLOCK_3_START = 148; - private static int BLOCK_4_START = 212; - private static int BLOCK_5_START = 276; - private static int BLOCK_6_START = 340; - private static int[] REVS = { 0,1,2,3 }; - private static int[] SYNC = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }; + private static int BLOCK_1_START = 20; + private static int BLOCK_2_START = 84; + private static int BLOCK_3_START = 148; + private static int BLOCK_4_START = 212; + private static int BLOCK_5_START = 276; + private static int BLOCK_6_START = 340; + private static int[] REVS = {0, 1, 2, 3}; + private static int[] SYNC = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; - /* Block 1 Fields */ - private static int[] B1_SYSDEF = { 21,22,23,24,25 }; - private static int[] B1_PREFIX = { 21,22,23,24,25,26,27 }; - private static int[] B1_TRAFFIC_CHANNEL = { 21,22,23,24,25,26,27,28,29,30 }; - private static int[] B1_SYSTEM_ID = { 26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 }; - private static int[] B1_IDENT1 = { 28,29,30,31,32,33,34,35,36,37,38,39,40 }; - private static int[] B1_CONTROL_CHANNEL = { 31,32,33,34,35,36,37,38,39,40 }; - private static int[] B1_CHANNEL = { 35,36,37,38,39,40,41,42,43,44 }; - private static int[] B1_MESSAGE_TYPE = { 41,42,43,44,45,46,47,48,49 }; - private static int[] B1_GTC_CHAN = { 43,44,45,46,47,48,49,50,51,52 }; - private static int[] B1_PREFIX2 = { 48,49,50,51,52,53,54 }; - private static int[] B1_IDENT2 = { 50,51,52,53,54,55,56,57,58,59,60,61,62 }; - private static int[] B1_IDENT2_GTC = { 53,54,55,56,57,58,59,60,61,62,63,64,65 }; - private static int[] B1_IDENT2_HEAD = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; - private static int[] B1_ADJSITE = { 49,50,51,52 }; - private static int B1_PERIODIC_CALL_MAINT_MESSAGES = 50; - private static int[] B1_MAINT_MESSAGE_INTERVAL = { 51,52,53,54,55 }; - private static int[] B1_WAIT_TIME = { 54,55,56 }; - private static int B1_PRESSEL_ON_REQUIRED = 56; - private static int[] B1_ALOHA_RSVD = { 57,58 }; - private static int[] B1_ALOHA_M = { 59,60,61,62,63 }; - private static int[] B1_ALOHA_N = { 64,65,66,67 }; - private static int B1_IDENT1_ID_VALUE = 57; - private static int[] B1_SLOTS = { 63,64 }; - private static int[] B1_DESCRIPTOR = { 65,66,67 }; - private static int[] B1_STATUS_MESSAGE = { 63,64,65,66,67 }; - - /* Block 2 or First Data Codeword Fields */ - private static int[] B2_SYSTEM_ID = { 85,86,87,88,89,90,91,92,93,94,95,96,97,98,99 }; - private static int[] B2_PREFIX = { 112,113,114,115,116,117,118 }; - private static int[] B2_IDENT2 = { 119,120,121,122,123,124,125,126,127,128,129,130,131 }; + /* Block 1 Fields */ + private static int[] B1_SYSDEF = {21, 22, 23, 24, 25}; + private static int[] B1_PREFIX = {21, 22, 23, 24, 25, 26, 27}; + private static int[] B1_TRAFFIC_CHANNEL = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + private static int[] B1_SYSTEM_ID = {26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}; + private static int[] B1_IDENT1 = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}; + private static int[] B1_CONTROL_CHANNEL = {31, 32, 33, 34, 35, 36, 37, 38, 39, 40}; + private static int[] B1_CHANNEL = {35, 36, 37, 38, 39, 40, 41, 42, 43, 44}; + private static int[] B1_MESSAGE_TYPE = {41, 42, 43, 44, 45, 46, 47, 48, 49}; + private static int[] B1_GTC_CHAN = {43, 44, 45, 46, 47, 48, 49, 50, 51, 52}; + private static int[] B1_PREFIX2 = {48, 49, 50, 51, 52, 53, 54}; + private static int[] B1_IDENT2 = {50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62}; + private static int[] B1_IDENT2_GTC = {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65}; + private static int[] B1_IDENT2_HEAD = {55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67}; + private static int[] B1_ADJSITE = {49, 50, 51, 52}; + private static int B1_PERIODIC_CALL_MAINT_MESSAGES = 50; + private static int[] B1_MAINT_MESSAGE_INTERVAL = {51, 52, 53, 54, 55}; + private static int[] B1_WAIT_TIME = {54, 55, 56}; + private static int B1_PRESSEL_ON_REQUIRED = 56; + private static int[] B1_ALOHA_RSVD = {57, 58}; + private static int[] B1_ALOHA_M = {59, 60, 61, 62, 63}; + private static int[] B1_ALOHA_N = {64, 65, 66, 67}; + private static int B1_IDENT1_ID_VALUE = 57; + private static int[] B1_SLOTS = {63, 64}; + private static int[] B1_DESCRIPTOR = {65, 66, 67}; + private static int[] B1_STATUS_MESSAGE = {63, 64, 65, 66, 67}; - private static int B2_SDM_SEGMENT_TRANSACTION_FLAG = 85; - private static int[] B2_SDM_GENERAL_FORMAT = { 86,87,88 }; - private static int B2_SDM_INITIAL_SEGMENT_FLAG = 89; - private static int B2_SDM_STF0_START = 86; - private static int B2_SDM_STF0_END = 131; - private static int B2_SDM_STF1_START = 90; - private static int B2_SDM_STF1_END = 131; + /* Block 2 or First Data Codeword Fields */ + private static int[] B2_SYSTEM_ID = {85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99}; + private static int[] B2_PREFIX = {112, 113, 114, 115, 116, 117, 118}; + private static int[] B2_IDENT2 = {119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131}; - /* Block 3 or Second Data Codeword Fields */ - private static int B3_SDM_RETURN_SLOT_ACCESS = 149; - private static int B3_SDM_START = 150; - private static int B3_SDM_END = 195; + private static int B2_SDM_SEGMENT_TRANSACTION_FLAG = 85; + private static int[] B2_SDM_GENERAL_FORMAT = {86, 87, 88}; + private static int B2_SDM_INITIAL_SEGMENT_FLAG = 89; + private static int B2_SDM_STF0_START = 86; + private static int B2_SDM_STF0_END = 131; + private static int B2_SDM_STF1_START = 90; + private static int B2_SDM_STF1_END = 131; - /* Block 4 or Third Data Codeword Fields */ - private static int B4_SDM_SEGMENT_TRANSACTION_FLAG = 213; - private static int[] B4_SDM_NUMBER_SEGMENTS = { 214,215 }; - private static int B4_SDM_CONTINUATION_SEGMENT_FLAG = 216; - private static int B4_SDM_RESERVED_FLAG = 217; - private static int B4_SDM_STF0_START = 214; - private static int B4_SDM_STF0_END = 259; - private static int B4_SDM_STF1_START = 218; - private static int B4_SDM_STF1_END = 259; - - /* Block 5 or Fourth Data Codeword Fields */ - private static int B5_SDM_RETURN_SLOT_ACCESS = 277; - private static int B5_SDM_START = 278; - private static int B5_SDM_END = 323; - - private static DecimalFormat mFrequencyFormatter = new DecimalFormat("#.#####"); - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); - Calendar mCalendar = new GregorianCalendar(); - - private BinaryMessage mMessage; - private CRC[] mCRC = new CRC[ 5 ]; - private AliasList mAliasList; + /* Block 3 or Second Data Codeword Fields */ + private static int B3_SDM_RETURN_SLOT_ACCESS = 149; + private static int B3_SDM_START = 150; + private static int B3_SDM_END = 195; + + /* Block 4 or Third Data Codeword Fields */ + private static int B4_SDM_SEGMENT_TRANSACTION_FLAG = 213; + private static int[] B4_SDM_NUMBER_SEGMENTS = {214, 215}; + private static int B4_SDM_CONTINUATION_SEGMENT_FLAG = 216; + private static int B4_SDM_RESERVED_FLAG = 217; + private static int B4_SDM_STF0_START = 214; + private static int B4_SDM_STF0_END = 259; + private static int B4_SDM_STF1_START = 218; + private static int B4_SDM_STF1_END = 259; + + /* Block 5 or Fourth Data Codeword Fields */ + private static int B5_SDM_RETURN_SLOT_ACCESS = 277; + private static int B5_SDM_START = 278; + private static int B5_SDM_END = 323; + + private BinaryMessage mMessage; + private CRC[] mCRC = new CRC[5]; private MPTMessageType mMessageType; - - public MPT1327Message( BinaryMessage message, AliasList list ) + + private MPT1327Talkgroup mFromIdentifier; + private MPT1327Talkgroup mToIdentifier; + private List mIdentifiers; + + public MPT1327Message(BinaryMessage message) { mMessage = message; - mAliasList = list; - checkParity( 0, BLOCK_1_START, BLOCK_2_START ); - - if( isValid() ) + checkParity(0, BLOCK_1_START, BLOCK_2_START); + + if(isValid()) { mMessageType = getMessageType(); - - switch( mMessageType ) + + switch(mMessageType) { - /* 1 data block messages */ - case AHYC: - case CLEAR: - case MAINT: - case MOVE: - break; - /* 2 data block messages */ - case AHYQ: - case ALH: - case HEAD_PLUS1: - checkParity( 1, BLOCK_2_START, BLOCK_3_START ); + /* 1 data block messages */ + case AHYC: + case CLEAR: + case MAINT: + case MOVE: break; - /* 3 data block messages */ - case HEAD_PLUS2: - checkParity( 1, BLOCK_2_START, BLOCK_3_START ); - checkParity( 2, BLOCK_3_START, BLOCK_4_START ); + /* 2 data block messages */ + case AHYQ: + case ALH: + case HEAD_PLUS1: + checkParity(1, BLOCK_2_START, BLOCK_3_START); break; - /* 4 data block messages */ - case ACKT: - case HEAD_PLUS3: - checkParity( 1, BLOCK_2_START, BLOCK_3_START ); - checkParity( 2, BLOCK_3_START, BLOCK_4_START ); - checkParity( 3, BLOCK_4_START, BLOCK_5_START ); + /* 3 data block messages */ + case HEAD_PLUS2: + checkParity(1, BLOCK_2_START, BLOCK_3_START); + checkParity(2, BLOCK_3_START, BLOCK_4_START); break; - /* 5 data block messages */ - case HEAD_PLUS4: - checkParity( 1, BLOCK_2_START, BLOCK_3_START ); - checkParity( 2, BLOCK_3_START, BLOCK_4_START ); - checkParity( 3, BLOCK_4_START, BLOCK_5_START ); - checkParity( 4, BLOCK_5_START, BLOCK_6_START ); + /* 4 data block messages */ + case ACKT: + case HEAD_PLUS3: + checkParity(1, BLOCK_2_START, BLOCK_3_START); + checkParity(2, BLOCK_3_START, BLOCK_4_START); + checkParity(3, BLOCK_4_START, BLOCK_5_START); + break; + /* 5 data block messages */ + case HEAD_PLUS4: + checkParity(1, BLOCK_2_START, BLOCK_3_START); + checkParity(2, BLOCK_3_START, BLOCK_4_START); + checkParity(3, BLOCK_4_START, BLOCK_5_START); + checkParity(4, BLOCK_5_START, BLOCK_6_START); + break; + case ACK: + case ACKB: + case ACKE: + case ACKI: + case ACKQ: + case ACKV: + case ACKX: + case AHOY: + case AHYP: + case AHYX: + case ALHD: + case ALHE: + case ALHF: + case ALHR: + case ALHS: + case ALHX: + case BCAST: + case GTC: + case MARK: + case DACKD: + case DACKZ: + case DACK_DAL: + case DACK_DALG: + case DACK_DALN: + case DACK_GO: + case DAHY: + case DAHYX: + case DAHYZ: + case DRQG: + case DRQX: + case DRQZ: + case GTT: + case RLA: + case SACK: + case SAMIS: + case SAMIU: + case SAMO: + case SITH: + case UNKN: + default: break; - case ACK: - case ACKB: - case ACKE: - case ACKI: - case ACKQ: - case ACKV: - case ACKX: - case AHOY: - case AHYP: - case AHYX: - case ALHD: - case ALHE: - case ALHF: - case ALHR: - case ALHS: - case ALHX: - case BCAST: - case GTC: - case MARK: - case DACKD: - case DACKZ: - case DACK_DAL: - case DACK_DALG: - case DACK_DALN: - case DACK_GO: - case DAHY: - case DAHYX: - case DAHYZ: - case DRQG: - case DRQX: - case DRQZ: - case GTT: - case RLA: - case SACK: - case SAMIS: - case SAMIU: - case SAMO: - case SITH: - case UNKN: - default: - break; } } else { - mMessageType = MPTMessageType.UNKN; + mMessageType = MPTMessageType.UNKN; } } - + /** - * Performs CRC check against the specified section/block, using the + * Performs CRC check against the specified section/block, using the * message bits between start and end. */ - private void checkParity( int section, int start, int end ) + private void checkParity(int section, int start, int end) { - mCRC[ section ] = detectAndCorrect( start, end ); + mCRC[section] = detectAndCorrect(start, end); } /** * Performs CRC check and corrects some bit errors. */ - - //TODO: move this to the CRC class - private CRC detectAndCorrect( int start, int end ) - { - BitSet original = mMessage.get( start, end ); - - CRC retVal = CRCFleetsync.check( original ); - - //Attempt to correct single-bit errors - if( retVal == CRC.FAILED_PARITY ) - { - int[] errorBitPositions = CRCFleetsync.findBitErrors( original ); - - if( errorBitPositions != null ) - { - for( int errorBitPosition: errorBitPositions ) - { - mMessage.flip( start + errorBitPosition ); - } - - retVal = CRC.CORRECTED; - } - } - - return retVal; - } - - /** - * String representing results of the parity check - * - * [P] = passes parity check - * [f] = fails parity check - * [C] = corrected message - * [-] = message section not present - */ - public String getParity() - { - return "[" + CRC.format( mCRC ) + "]"; - } - /** - * Indicates if Block 1 of the message has passed the CRC check. - */ + //TODO: move this to the CRC class + private CRC detectAndCorrect(int start, int end) + { + BitSet original = mMessage.get(start, end); + + CRC retVal = CRCFleetsync.check(original); + + //Attempt to correct single-bit errors + if(retVal == CRC.FAILED_PARITY) + { + int[] errorBitPositions = CRCFleetsync.findBitErrors(original); + + if(errorBitPositions != null) + { + for(int errorBitPosition : errorBitPositions) + { + mMessage.flip(start + errorBitPosition); + } + + retVal = CRC.CORRECTED; + } + } + + return retVal; + } + + /** + * String representing results of the parity check + * + * [P] = passes parity check + * [f] = fails parity check + * [C] = corrected message + * [-] = message section not present + */ + public String getParity() + { + return "[" + CRC.format(mCRC) + "]"; + } + + /** + * Indicates if Block 1 of the message has passed the CRC check. + */ public boolean isValid() { - return mCRC[ 0 ] == CRC.PASSED || - mCRC[ 0 ] == CRC.CORRECTED; + return mCRC[0] == CRC.PASSED || + mCRC[0] == CRC.CORRECTED; } - + public boolean isValidCall() { - return getPrefix() != 0 && - getIdent1() != 0 && - getIdent2() != 0; + return getPrefix() != 0 && + getIdent1() != 0 && + getIdent2() != 0; } /** @@ -296,23 +280,7 @@ public class MPT1327Message extends Message @Override public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append( mSDF.format( new Date() ) ); - - sb.append( " MPT1327 " ); - - sb.append( getParity() ); - - sb.append( " " ); - - sb.append( getMessage() ); - - sb.append( getFiller( sb, 100 ) ); - - sb.append( " [" + mMessage.toString() + "]" ); - - return sb.toString(); + return getMessage(); } /** @@ -320,9 +288,9 @@ public class MPT1327Message extends Message */ public MPTMessageType getMessageType() { - int value = mMessage.getInt( B1_MESSAGE_TYPE ); + int value = mMessage.getInt(B1_MESSAGE_TYPE); - return MPTMessageType.fromNumber( value ); + return MPTMessageType.fromNumber(value); } /** @@ -330,18 +298,18 @@ public class MPT1327Message extends Message */ public String getSiteID() { - if( mMessageType == MPTMessageType.BCAST ) - { - return String.valueOf( mMessage.getInt( B1_SYSTEM_ID ) ); - } - else if( mMessageType == MPTMessageType.ALH ) - { - return String.valueOf( mMessage.getInt( B2_SYSTEM_ID ) ); - } - else - { - return null; - } + if(mMessageType == MPTMessageType.BCAST) + { + return String.valueOf(mMessage.getInt(B1_SYSTEM_ID)); + } + else if(mMessageType == MPTMessageType.ALH) + { + return String.valueOf(mMessage.getInt(B2_SYSTEM_ID)); + } + else + { + return null; + } } /** @@ -349,1773 +317,1731 @@ public class MPT1327Message extends Message */ public boolean hasSystemID() { - return getSiteID() != null; + return getSiteID() != null; } - + public SystemDefinition getSystemDefinition() { - int sysdef = mMessage.getInt( B1_SYSDEF ); - - return SystemDefinition.fromNumber( sysdef ); + int sysdef = mMessage.getInt(B1_SYSDEF); + + return SystemDefinition.fromNumber(sysdef); } - + public boolean getPeriodicMaintenanceMessagesRequired() { - return mMessage.get( B1_PERIODIC_CALL_MAINT_MESSAGES ); + return mMessage.get(B1_PERIODIC_CALL_MAINT_MESSAGES); } - + public int getMaintenanceMessageInterval() { - return mMessage.getInt( B1_MAINT_MESSAGE_INTERVAL ); + return mMessage.getInt(B1_MAINT_MESSAGE_INTERVAL); } - + public boolean getPresselOnRequired() { - return mMessage.get( B1_PRESSEL_ON_REQUIRED ); + return mMessage.get(B1_PRESSEL_ON_REQUIRED); } - + public String getMaintMessageIDENT1Value() { - return ( mMessage.get( B1_IDENT1_ID_VALUE ) ? "GROUP ADDRESS" : - "INDIVIDUAL ADDRESS" ); + return (mMessage.get(B1_IDENT1_ID_VALUE) ? "GROUP ADDRESS" : + "INDIVIDUAL ADDRESS"); } - + public int getChannel() { - switch( mMessageType ) - { - case CLEAR: - return mMessage.getInt( B1_TRAFFIC_CHANNEL ); - case GTC: - return mMessage.getInt( B1_GTC_CHAN ); - default: - return mMessage.getInt( B1_CHANNEL ); - } + switch(mMessageType) + { + case CLEAR: + return mMessage.getInt(B1_TRAFFIC_CHANNEL); + case GTC: + return mMessage.getInt(B1_GTC_CHAN); + default: + return mMessage.getInt(B1_CHANNEL); + } } - + public int getReturnToChannel() { - if( mMessageType == MPTMessageType.CLEAR ) - { - return mMessage.getInt( B1_CONTROL_CHANNEL ); - } - else - { - return 0; - } + if(mMessageType == MPTMessageType.CLEAR) + { + return mMessage.getInt(B1_CONTROL_CHANNEL); + } + else + { + return 0; + } } - + public int getAdjacentSiteSerialNumber() { - return mMessage.getInt( B1_ADJSITE ); + return mMessage.getInt(B1_ADJSITE); } public int getPrefix() { - return mMessage.getInt( B1_PREFIX ); + return mMessage.getInt(B1_PREFIX); } - + public int getBlock2Prefix() { - return mMessage.getInt( B2_PREFIX ); + return mMessage.getInt(B2_PREFIX); } - + public int getIdent1() { - return mMessage.getInt( B1_IDENT1 ); + return mMessage.getInt(B1_IDENT1); } - + public IdentType getIdent1Type() { - return IdentType.fromIdent( getIdent1() ); + return IdentType.fromIdent(getIdent1()); } - + public int getIdent2() { - MPTMessageType type = getMessageType(); - - if( type == MPTMessageType.GTC ) - { - return mMessage.getInt( B1_IDENT2_GTC ); - } - else if( type == MPTMessageType.HEAD_PLUS1 || - type == MPTMessageType.HEAD_PLUS2 || - type == MPTMessageType.HEAD_PLUS3 || - type == MPTMessageType.HEAD_PLUS4 ) - { - return mMessage.getInt( B1_IDENT2_HEAD ); - } - else - { - return mMessage.getInt( B1_IDENT2 ); - } + MPTMessageType type = getMessageType(); + + if(type == MPTMessageType.GTC) + { + return mMessage.getInt(B1_IDENT2_GTC); + } + else if(type == MPTMessageType.HEAD_PLUS1 || + type == MPTMessageType.HEAD_PLUS2 || + type == MPTMessageType.HEAD_PLUS3 || + type == MPTMessageType.HEAD_PLUS4) + { + return mMessage.getInt(B1_IDENT2_HEAD); + } + else + { + return mMessage.getInt(B1_IDENT2); + } } - + public IdentType getIdent2Type() { - return IdentType.fromIdent( getIdent2() ); + return IdentType.fromIdent(getIdent2()); } - + public int getBlock2Ident2() { - return mMessage.getInt( B2_IDENT2 ); + return mMessage.getInt(B2_IDENT2); } - + public String getStatusMessage() { - int status = mMessage.getInt( B1_STATUS_MESSAGE ); - - switch( status ) - { - case 0: - return "STATUS: Request Speech Call"; - case 31: - return "STATUS: Cancel Request Speech Call"; - default: - return "STATUS: " + status; - } + int status = mMessage.getInt(B1_STATUS_MESSAGE); + + switch(status) + { + case 0: + return "STATUS: Request Speech Call"; + case 31: + return "STATUS: Cancel Request Speech Call"; + default: + return "STATUS: " + status; + } } - + /** * Returns spaces to the fill the string builder to ensure length is >= index */ - public String getFiller( StringBuilder sb, int index ) + public String getFiller(StringBuilder sb, int index) { - if( sb.length() < index ) - { - return String.format( "%" + ( index - sb.length() ) + "s", " " ); - } - else - { - return ""; - } + if(sb.length() < index) + { + return String.format("%" + (index - sb.length()) + "s", " "); + } + else + { + return ""; + } } - + /** * Pads spaces onto the end of the value to make it 'places' long */ - public String pad( String value, int places, String padCharacter ) + public String pad(String value, int places, String padCharacter) { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(value); + + while(sb.length() < places) + { + sb.append(padCharacter); + } + + return sb.toString(); } - + /** * Pads an integer value with additional zeroes to make it decimalPlaces long */ - public String format( int number, int decimalPlaces ) + public String format(int number, int decimalPlaces) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); + int paddingRequired = decimalPlaces - (String.valueOf(number).length()); - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); + for(int x = 0; x < paddingRequired; x++) + { + sb.append("0"); + } + + sb.append(number); + + return sb.toString(); } - - @Override + public String getMessage() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( pad( mMessageType.toString(), 5, " " ) ); - - switch( mMessageType ) - { - case ACK: - sb.append( " ACKNOWLEDGE " ); + sb.append(pad(mMessageType.toString(), 5, " ")); - if( hasFromID() ) - { - sb.append( getFromID() ); - } + switch(mMessageType) + { + case ACK: + sb.append(" ACKNOWLEDGE "); - IdentType type = getIdent1Type(); - - switch( type ) - { - case ALLI: - case IPFIXI: - case PABXI: - case PSTNGI: - case PSTNSI1: - case PSTNSI2: - case PSTNSI3: - case PSTNSI4: - case PSTNSI5: - case PSTNSI6: - case PSTNSI7: - case PSTNSI8: - case PSTNSI9: - case PSTNSI10: - case PSTNSI11: - case PSTNSI12: - case PSTNSI13: - case PSTNSI14: - case PSTNSI15: - case USER: - sb.append( type.getLabel() ); - sb.append( " CALL REQUEST" ); - break; - case TSCI: - sb.append( " RQQ or RQC TRANSACTION" ); - break; - case DIVERTI: - sb.append( " CANCELLATION OF CALL DIVERSION REQUEST" ); - break; - default: - sb.append( " " ); - sb.append( type.getLabel() ); - sb.append( " REQUEST" ); - break; - } - break; - case ACKI: - sb.append( " MESSAGE ACKNOWLEDGED - MORE TO FOLLOW" ); + if(hasFromID()) + { + sb.append(getFromID()); + } - if( hasFromID() ) - { - sb.append( " FROM:" ); - sb.append( getFromID() ); - } - - if( hasToID() ) - { - sb.append( " TO:" ); - sb.append( getToID() ); - } - break; - case ACKT: - sb.append( " SITE:" ); - sb.append( getSiteID() ); + IdentType type = getIdent1Type(); - sb.append( " LONG ACK MESSAGE" ); + switch(type) + { + case ALLI: + case IPFIXI: + case PABXI: + case PSTNGI: + case PSTNSI1: + case PSTNSI2: + case PSTNSI3: + case PSTNSI4: + case PSTNSI5: + case PSTNSI6: + case PSTNSI7: + case PSTNSI8: + case PSTNSI9: + case PSTNSI10: + case PSTNSI11: + case PSTNSI12: + case PSTNSI13: + case PSTNSI14: + case PSTNSI15: + case USER: + sb.append(type.getLabel()); + sb.append(" CALL REQUEST"); + break; + case TSCI: + sb.append(" RQQ or RQC TRANSACTION"); + break; + case DIVERTI: + sb.append(" CANCELLATION OF CALL DIVERSION REQUEST"); + break; + default: + sb.append(" "); + sb.append(type.getLabel()); + sb.append(" REQUEST"); + break; + } + break; + case ACKI: + sb.append(" MESSAGE ACKNOWLEDGED - MORE TO FOLLOW"); - if( hasFromID() ) - { - sb.append( " FROM:" ); - sb.append( getFromID() ); - } - - if( hasToID() ) - { - sb.append( " TO:" ); - sb.append( getToID() ); - } - - sb.append( "**********************************" ); - break; - case ACKQ: - sb.append( " SYSTEM:" ); - sb.append( getSiteID() ); + if(hasFromID()) + { + sb.append(" FROM:"); + sb.append(getFromID()); + } - sb.append( " CALL QUEUED FROM:" ); + if(hasToID()) + { + sb.append(" TO:"); + sb.append(getToID()); + } + break; + case ACKT: + sb.append(" SITE:"); + sb.append(getSiteID()); - if( hasFromID() ) - { - sb.append( " FROM:" ); - sb.append( getFromID() ); - } - - if( hasToID() ) - { - sb.append( " TO:" ); - sb.append( getToID() ); - } - break; - case ACKX: - sb.append( " SYSTEM:" ); - sb.append( getSiteID() ); + sb.append(" LONG ACK MESSAGE"); - sb.append( " MESSAGE REJECTED FROM:" ); + if(hasFromID()) + { + sb.append(" FROM:"); + sb.append(getFromID()); + } - if( hasFromID() ) - { - sb.append( " FROM:" ); - sb.append( getFromID() ); - } - - if( hasToID() ) - { - sb.append( " TO:" ); - sb.append( getToID() ); - } - break; - case AHYC: - sb.append( " FM:" ); - sb.append( getFromID() ); - sb.append( " TO:" ); - sb.append( getToID() ); - sb.append( " " ); - sb.append( getRequestString() ); - break; - case AHYQ: - /* Status Message */ - sb.append( " STATUS MESSAGE" ); + if(hasToID()) + { + sb.append(" TO:"); + sb.append(getToID()); + } - if( hasFromID() ) - { - sb.append( " FROM:" ); - sb.append( getFromID() ); - } - - if( hasToID() ) - { - sb.append( " TO:" ); - sb.append( getToID() ); - } - sb.append( " " ); - sb.append( getStatusMessage() ); - break; - case ALH: - case ALHD: - case ALHS: - case ALHE: - case ALHR: - case ALHX: - case ALHF: - sb.append( " SYSTEM:" ); - sb.append( getSiteID() ); + sb.append("**********************************"); + break; + case ACKQ: + sb.append(" SYSTEM:"); + sb.append(getSiteID()); - if( hasToID() ) - { - sb.append( " ID:" ); - sb.append( getToID() ); - } - - sb.append( " WAIT:" ); - sb.append( getWaitTime() ); - sb.append( " RSVD:" ); - sb.append( getAlohaReserved() ); - sb.append( " M:" ); - sb.append( getAlohaM() ); - sb.append( " N:" ); - sb.append( getAlohaN() ); - break; - case BCAST: - sb.append( " SYSTEM:" ); - sb.append( getSiteID() ); + sb.append(" CALL QUEUED FROM:"); - SystemDefinition sysdef = getSystemDefinition(); + if(hasFromID()) + { + sb.append(" FROM:"); + sb.append(getFromID()); + } - sb.append( " " ); - sb.append( sysdef.getLabel() ); - - switch( sysdef ) - { - case ANNOUNCE_CONTROL_CHANNEL: - case WITHDRAW_CONTROL_CHANNEL: - sb.append( " CHAN:" ); - sb.append( getChannel() ); - break; - case BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER: - sb.append( " CHAN:" ); - sb.append( getChannel() ); - sb.append( " SER:" ); - sb.append( getAdjacentSiteSerialNumber() ); - break; - case SPECIFY_CALL_MAINTENANCE_PARAMETERS: - sb.append( " PERIODIC MAINT MSG REQD:" ); - sb.append( getPeriodicMaintenanceMessagesRequired() ); - sb.append( " INTERVAL:" ); - sb.append( getMaintenanceMessageInterval() ); - sb.append( "(sec) PRESSEL ON REQD:" ); - sb.append( getPresselOnRequired() ); - sb.append( " IDENT1 VALUE:" ); - sb.append( getMaintMessageIDENT1Value() ); - default: - break; - } - break; - case CLEAR: - sb.append( " TRAFFIC CHANNEL:" ); - sb.append( getChannel() ); - sb.append( " RETURN TO CONTROL CHANNEL:" ); - sb.append( getReturnToChannel() ); - break; - case GTC: - if( hasFromID() ) - { - sb.append( " FROM:" ); - sb.append( getFromID() ); - } + if(hasToID()) + { + sb.append(" TO:"); + sb.append(getToID()); + } + break; + case ACKX: + sb.append(" SYSTEM:"); + sb.append(getSiteID()); - if( hasToID() ) - { - sb.append( " TO:" ); - sb.append( getToID() ); - } - - sb.append( " CHAN:" ); - sb.append( getChannel() ); - break; - case MAINT: - if( hasToID() ) - { - sb.append( " ID:" ); - sb.append( getToID() ); - } - - break; - case HEAD_PLUS1: - case HEAD_PLUS2: - case HEAD_PLUS3: - case HEAD_PLUS4: - sb.append( " " ); - sb.append( getSDM() ); - break; - default: - break; - } - - return sb.toString(); + sb.append(" MESSAGE REJECTED FROM:"); + + if(hasFromID()) + { + sb.append(" FROM:"); + sb.append(getFromID()); + } + + if(hasToID()) + { + sb.append(" TO:"); + sb.append(getToID()); + } + break; + case AHYC: + sb.append(" FM:"); + sb.append(getFromID()); + sb.append(" TO:"); + sb.append(getToID()); + sb.append(" "); + sb.append(getRequestString()); + break; + case AHYQ: + /* Status Message */ + sb.append(" STATUS MESSAGE"); + + if(hasFromID()) + { + sb.append(" FROM:"); + sb.append(getFromID()); + } + + if(hasToID()) + { + sb.append(" TO:"); + sb.append(getToID()); + } + sb.append(" "); + sb.append(getStatusMessage()); + break; + case ALH: + case ALHD: + case ALHS: + case ALHE: + case ALHR: + case ALHX: + case ALHF: + sb.append(" SYSTEM:"); + sb.append(getSiteID()); + + if(hasToID()) + { + sb.append(" ID:"); + sb.append(getToID()); + } + + sb.append(" WAIT:"); + sb.append(getWaitTime()); + sb.append(" RSVD:"); + sb.append(getAlohaReserved()); + sb.append(" M:"); + sb.append(getAlohaM()); + sb.append(" N:"); + sb.append(getAlohaN()); + break; + case BCAST: + sb.append(" SYSTEM:"); + sb.append(getSiteID()); + + SystemDefinition sysdef = getSystemDefinition(); + + sb.append(" "); + sb.append(sysdef.getLabel()); + + switch(sysdef) + { + case ANNOUNCE_CONTROL_CHANNEL: + case WITHDRAW_CONTROL_CHANNEL: + sb.append(" CHAN:"); + sb.append(getChannel()); + break; + case BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER: + sb.append(" CHAN:"); + sb.append(getChannel()); + sb.append(" SER:"); + sb.append(getAdjacentSiteSerialNumber()); + break; + case SPECIFY_CALL_MAINTENANCE_PARAMETERS: + sb.append(" PERIODIC MAINT MSG REQD:"); + sb.append(getPeriodicMaintenanceMessagesRequired()); + sb.append(" INTERVAL:"); + sb.append(getMaintenanceMessageInterval()); + sb.append("(sec) PRESSEL ON REQD:"); + sb.append(getPresselOnRequired()); + sb.append(" IDENT1 VALUE:"); + sb.append(getMaintMessageIDENT1Value()); + default: + break; + } + break; + case CLEAR: + sb.append(" TRAFFIC CHANNEL:"); + sb.append(getChannel()); + sb.append(" RETURN TO CONTROL CHANNEL:"); + sb.append(getReturnToChannel()); + break; + case GTC: + if(hasFromID()) + { + sb.append(" FROM:"); + sb.append(getFromID()); + } + + if(hasToID()) + { + sb.append(" TO:"); + sb.append(getToID()); + } + + sb.append(" CHAN:"); + sb.append(getChannel()); + break; + case MAINT: + if(hasToID()) + { + sb.append(" ID:"); + sb.append(getToID()); + } + + break; + case HEAD_PLUS1: + case HEAD_PLUS2: + case HEAD_PLUS3: + case HEAD_PLUS4: + sb.append(" "); + sb.append(getSDM()); + break; + default: + break; + } + + return sb.toString(); } - @Override - public String getBinaryMessage() + @Override + public Protocol getProtocol() { - return mMessage.toString(); + return Protocol.MPT1327; } - @Override - public String getProtocol() + /** + * Translates the AHYC request from the values in ident1 and ident2 + */ + public String getRequestString() { - return "MPT-1327"; + StringBuilder sb = new StringBuilder(); + + IdentType type2 = getIdent2Type(); + + if(type2 == IdentType.USER) + { + IdentType type1 = getIdent1Type(); + + Slots slots = getSlots(); + + switch(type1) + { + case DIVERTI: + sb.append("SEND BLOCKED ADDRESS FOR THIRD-PARTY CALL DIVERSION USING "); + sb.append(slots.getLabel()); + break; + case IPFIXI: + sb.append("SEND INTER-PREFIX CALL EXTENDED ADDRESSING INFORMATION USING "); + sb.append(slots.getLabel()); + break; + case PABXI: + sb.append("SEND PABX EXTENSION USING"); + sb.append(slots.getLabel()); + break; + case PSTNGI: + if(slots == Slots.SLOTS_1) + { + sb.append("SEND UP TO 9 PSTN DIALED DIGITS"); + } + else if(slots == Slots.SLOTS_2) + { + sb.append("SEND 10 TO 31 PSTN DIALED DIGITS"); + } + else + { + sb.append("SEND PSTN DIALED DIGITS USING "); + sb.append(slots.getLabel()); + } + break; + case SDMI: + sb.append("SEND SHORT DATA MESSAGE USING "); + sb.append(slots.getLabel()); + break; + default: + break; + } + } + else + { + /* MODE 2 */ + sb.append("SEND "); + sb.append(getDescriptor().getMode2Label()); + } + + return sb.toString(); } - @Override - public String getEventType() - { - // TODO Auto-generated method stub - return null; - } - - /** - * Translates the AHYC request from the values in ident1 and ident2 - */ - public String getRequestString() - { - StringBuilder sb = new StringBuilder(); - - IdentType type2 = getIdent2Type(); - - if( type2 == IdentType.USER ) - { - IdentType type1 = getIdent1Type(); - - Slots slots = getSlots(); - - switch( type1 ) - { - case DIVERTI: - sb.append( "SEND BLOCKED ADDRESS FOR THIRD-PARTY CALL DIVERSION USING " ); - sb.append( slots.getLabel() ); - break; - case IPFIXI: - sb.append( "SEND INTER-PREFIX CALL EXTENDED ADDRESSING INFORMATION USING " ); - sb.append( slots.getLabel() ); - break; - case PABXI: - sb.append( "SEND PABX EXTENSION USING" ); - sb.append( slots.getLabel() ); - break; - case PSTNGI: - if( slots == Slots.SLOTS_1 ) - { - sb.append( "SEND UP TO 9 PSTN DIALED DIGITS" ); - } - else if( slots == Slots.SLOTS_2 ) - { - sb.append( "SEND 10 TO 31 PSTN DIALED DIGITS" ); - } - else - { - sb.append( "SEND PSTN DIALED DIGITS USING " ); - sb.append( slots.getLabel() ); - } - break; - case SDMI: - sb.append( "SEND SHORT DATA MESSAGE USING " ); - sb.append( slots.getLabel() ); - break; - default: - break; - } - } - else - { - /* MODE 2 */ - sb.append( "SEND " ); - sb.append( getDescriptor().getMode2Label() ); - } - - return sb.toString(); - } - - /** - * Constructs a FROM identifier from the Prefix2 and Ident2 values - */ - @Override + /** + * Constructs a FROM identifier from the Prefix2 and Ident2 values + */ public String getFromID() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - int ident2 = getIdent2(); - - IdentType type = IdentType.fromIdent( ident2 ); + int ident2 = getIdent2(); - /* Inter-Prefix - the from and to idents are different prefixes */ - if( type == IdentType.IPFIXI ) - { - sb.append( format( getBlock2Prefix(), 3 ) ); - sb.append( "-" ); - sb.append( format( getBlock2Ident2(), 4) ); - } - else - { - sb.append( format( getPrefix(), 3 ) ); - sb.append( "-" ); - sb.append( format( ident2, 4) ); - } - - return sb.toString(); + IdentType type = IdentType.fromIdent(ident2); + + /* Inter-Prefix - the from and to idents are different prefixes */ + if(type == IdentType.IPFIXI) + { + sb.append(format(getBlock2Prefix(), 3)); + sb.append("-"); + sb.append(format(getBlock2Ident2(), 4)); + } + else + { + sb.append(format(getPrefix(), 3)); + sb.append("-"); + sb.append(format(ident2, 4)); + } + + return sb.toString(); } - - public boolean hasFromID() - { - return getFromID() != null && !getFromID().isEmpty(); - } - @Override - public Alias getFromIDAlias() + public boolean hasFromID() { - if( mAliasList != null && hasFromID() ) - { - return mAliasList.getMPT1327Alias( getFromID() ); - } - - return null; + return getFromID() != null && !getFromID().isEmpty(); } - /** - * Constructs a TO identifier from the Prefix1 and Ident1 fields - */ - @Override + /** + * Constructs a TO identifier from the Prefix1 and Ident1 fields + */ public String getToID() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - int prefix = getPrefix(); - int ident = getIdent1(); + int prefix = getPrefix(); + int ident = getIdent1(); - switch( IdentType.fromIdent( ident ) ) - { - case IPFIXI: - sb.append( "INTER-PREFIX" ); - break; - case ALLI: - sb.append( "ALL RADIOS" ); - break; - case PABXI: - sb.append( "PABX EXT" ); - break; - case PSTNSI1: - case PSTNSI2: - case PSTNSI3: - case PSTNSI4: - case PSTNSI5: - case PSTNSI6: - case PSTNSI7: - case PSTNSI8: - case PSTNSI9: - case PSTNSI10: - case PSTNSI11: - case PSTNSI12: - case PSTNSI13: - case PSTNSI14: - case PSTNSI15: - sb.append( "PRE-DEFINED PSTN" ); - break; - case PSTNGI: - sb.append( "PSTN GATEWAY" ); - break; - case TSCI: - sb.append( "SYSTEM CONTROLLER" ); - break; - case DIVERTI: - sb.append( "CALL DIVERT" ); - break; - case USER: - default: - if( prefix != 0 || ident != 0 ) - { - sb.append( format( prefix, 3 ) ); - sb.append( "-" ); - sb.append( format( ident, 4) ); - } - break; - } + switch(IdentType.fromIdent(ident)) + { + case IPFIXI: + sb.append("INTER-PREFIX"); + break; + case ALLI: + sb.append("ALL RADIOS"); + break; + case PABXI: + sb.append("PABX EXT"); + break; + case PSTNSI1: + case PSTNSI2: + case PSTNSI3: + case PSTNSI4: + case PSTNSI5: + case PSTNSI6: + case PSTNSI7: + case PSTNSI8: + case PSTNSI9: + case PSTNSI10: + case PSTNSI11: + case PSTNSI12: + case PSTNSI13: + case PSTNSI14: + case PSTNSI15: + sb.append("PRE-DEFINED PSTN"); + break; + case PSTNGI: + sb.append("PSTN GATEWAY"); + break; + case TSCI: + sb.append("SYSTEM CONTROLLER"); + break; + case DIVERTI: + sb.append("CALL DIVERT"); + break; + case USER: + default: + if(prefix != 0 || ident != 0) + { + sb.append(format(prefix, 3)); + sb.append("-"); + sb.append(format(ident, 4)); + } + break; + } - return sb.toString(); + return sb.toString(); } - - public boolean hasToID() - { - return getToID() != null && !getToID().isEmpty(); - } - /** - * Data message codeword descriptor. Indicates the type of data message - * that the radio unit shall respond with. - */ - public Descriptor getDescriptor() - { - return Descriptor.fromNumber( mMessage.getInt( B1_DESCRIPTOR ) ); - } - - /** - * Indicates the number of data codeword slots that are appended to a HEAD - * message - */ - public Slots getSlots() - { - return Slots.fromNumber( mMessage.getInt( B1_SLOTS ) ); - } - - @Override - public Alias getToIDAlias() + public boolean hasToID() { - if( hasToID() ) - { - return mAliasList.getMPT1327Alias( getToID() ); - } - else - { - return null; - } + return getToID() != null && !getToID().isEmpty(); } - - /** - * Aloha wait time - random access protocol - */ - public int getWaitTime() - { - return mMessage.getInt( B1_WAIT_TIME ); - } - /** - * Aloha reserved field - */ - public int getAlohaReserved() - { - return mMessage.getInt( B1_ALOHA_RSVD ); - } + /** + * Data message codeword descriptor. Indicates the type of data message + * that the radio unit shall respond with. + */ + public Descriptor getDescriptor() + { + return Descriptor.fromNumber(mMessage.getInt(B1_DESCRIPTOR)); + } - /** - * Aloha M field - */ - public int getAlohaM() - { - return mMessage.getInt( B1_ALOHA_M ); - } + /** + * Indicates the number of data codeword slots that are appended to a HEAD + * message + */ + public Slots getSlots() + { + return Slots.fromNumber(mMessage.getInt(B1_SLOTS)); + } - /** - * Aloha N field - */ - public int getAlohaN() - { - return mMessage.getInt( B1_ALOHA_N ); - } + /** + * Aloha wait time - random access protocol + */ + public int getWaitTime() + { + return mMessage.getInt(B1_WAIT_TIME); + } - /** - * String representation of the CRC check for each 64-bit message block in - * this message. - */ - @Override + /** + * Aloha reserved field + */ + public int getAlohaReserved() + { + return mMessage.getInt(B1_ALOHA_RSVD); + } + + /** + * Aloha M field + */ + public int getAlohaM() + { + return mMessage.getInt(B1_ALOHA_M); + } + + /** + * Aloha N field + */ + public int getAlohaN() + { + return mMessage.getInt(B1_ALOHA_N); + } + + /** + * String representation of the CRC check for each 64-bit message block in + * this message. + */ public String getErrorStatus() { - return getParity(); + return getParity(); } - /** - * Used by the map to get a plottable version of this event - */ - @Override - public Plottable getPlottable() + /** + * Indicates if the received SDM is formatted according to the MPT-1327 or + * the MPT-1343 ICD. + */ + public SDMFormat getSDMFormat() { - return null; + return SDMFormat.valueOf(mMessage.get(B2_SDM_SEGMENT_TRANSACTION_FLAG), + mMessage.getInt(B2_SDM_GENERAL_FORMAT)); } - - /** - * Indicates if the received SDM is formatted according to the MPT-1327 or - * the MPT-1343 ICD. - */ - public SDMFormat getSDMFormat() - { - return SDMFormat.valueOf( mMessage.get( B2_SDM_SEGMENT_TRANSACTION_FLAG ), - mMessage.getInt( B2_SDM_GENERAL_FORMAT ) ); - } - - public String getSDM() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "SDM:" ); - - MPTMessageType type = getMessageType(); - switch( type ) - { - case HEAD_PLUS1: - case HEAD_PLUS2: - case HEAD_PLUS3: - case HEAD_PLUS4: - SDMFormat format = getSDMFormat(); - - switch( format ) - { - case MPT1327: - sb.append( "MPT1327 " ); - sb.append( getSDMBinary( type, format ) ); - break; - case MPT1343_BINARY: - sb.append( "BINARY " ); - sb.append( getSDMBinary( type, format ) ); - break; - case MPT1343_COMMAND: - sb.append( "COMMAND " ); - sb.append( getSDMBinary( type, format ) ); - break; - case MPT1343_MAP27: - sb.append( "MAP27 " ); - sb.append( getSDMBinary( type, format ) ); - break; - case MPT1343_RESERVED: - sb.append( "RESERVED " ); - sb.append( getSDMBinary( type, format ) ); - break; - case MPT1343_SPARE: - sb.append( "SPARE " ); - sb.append( getSDMBinary( type, format ) ); - break; - case MPT1343_ASCII: - sb.append( "ASCII " ); - sb.append( getSDMASCII( type ) ); - break; - case MPT1343_BCD: - sb.append( "BCD " ); - sb.append( getSDMBCD( type ) ); - break; - case MPT1343_TELEX: - sb.append( "TELEX " ); - sb.append( getSDMTelex( type ) ); - break; - case UNKNOWN: - default: - sb.append( "UNKNOWN " ); - break; - } - - break; - case SAMO: - sb.append( "SAMO " ); - break; - case SAMIS: - sb.append( "SAMIS " ); - break; - case SAMIU: - sb.append( "SAMIU " ); - break; - default: - break; - - } - - return sb.toString(); - } - - public String getSDMBinary( MPTMessageType type, SDMFormat format ) - { - StringBuilder sb = new StringBuilder(); - - switch( type ) - { - case HEAD_PLUS1: - if( format == SDMFormat.MPT1327 ) - { - sb.append( mMessage.getHex( B2_SDM_STF0_START, - B2_SDM_STF0_END, - 12 ) ); - } - else - { - sb.append( mMessage.getHex( B2_SDM_STF1_START, - B2_SDM_STF1_END, - 12 ) ); - } - break; - case HEAD_PLUS2: - if( format == SDMFormat.MPT1327 ) - { - sb.append( mMessage.getHex( B2_SDM_STF0_START, - B2_SDM_STF0_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B3_SDM_START, - B3_SDM_END, - 12 ) ); - } - else - { - sb.append( mMessage.getHex( B2_SDM_STF1_START, - B2_SDM_STF1_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B3_SDM_START, - B3_SDM_END, - 12 ) ); - } - break; - case HEAD_PLUS3: - if( format == SDMFormat.MPT1327 ) - { - sb.append( mMessage.getHex( B2_SDM_STF0_START, - B2_SDM_STF0_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B3_SDM_START, - B3_SDM_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B4_SDM_STF0_START, - B4_SDM_STF0_END, - 12 ) ); - } - else - { - sb.append( mMessage.getHex( B2_SDM_STF1_START, - B2_SDM_STF1_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B3_SDM_START, - B3_SDM_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B4_SDM_STF1_START, - B4_SDM_STF1_END, - 12 ) ); - } - break; - case HEAD_PLUS4: - if( format == SDMFormat.MPT1327 ) - { - sb.append( mMessage.getHex( B2_SDM_STF0_START, - B2_SDM_STF0_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B3_SDM_START, - B3_SDM_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B4_SDM_STF0_START, - B4_SDM_STF0_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B5_SDM_START, - B5_SDM_END, - 12 ) ); - } - else - { - sb.append( mMessage.getHex( B2_SDM_STF1_START, - B2_SDM_STF1_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B3_SDM_START, - B3_SDM_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B4_SDM_STF1_START, - B4_SDM_STF1_END, - 12 ) ); - sb.append( " " ); - sb.append( mMessage.getHex( B5_SDM_START, - B5_SDM_END, - 12 ) ); - } - break; - default: - break; - } - - return sb.toString(); - } + public String getSDM() + { + StringBuilder sb = new StringBuilder(); - public String getSDMASCII( MPTMessageType type ) - { - StringBuilder sb = new StringBuilder(); - - int blocks = 0; - - switch( type ) - { - case HEAD_PLUS1: - blocks = 1; - break; - case HEAD_PLUS2: - blocks = 2; - break; - case HEAD_PLUS3: - blocks = 3; - break; - case HEAD_PLUS4: - blocks = 4; - break; - default: - break; - } - - if( blocks >= 1 ) - { - sb.append( (char)mMessage.getInt( 91, 97 ) ); - sb.append( (char)mMessage.getInt( 98, 104 ) ); - sb.append( (char)mMessage.getInt( 105, 111 ) ); - sb.append( (char)mMessage.getInt( 112, 118 ) ); - sb.append( (char)mMessage.getInt( 119, 125 ) ); - } - - if( blocks >= 2 ) - { - sb.append( (char)mMessage.getInt( - new int[] { 126,127,128,129,130,131,150 } ) ); - sb.append( (char)mMessage.getInt( 151, 157 ) ); - sb.append( (char)mMessage.getInt( 158, 164 ) ); - sb.append( (char)mMessage.getInt( 165, 171 ) ); - sb.append( (char)mMessage.getInt( 172, 178 ) ); - sb.append( (char)mMessage.getInt( 179, 185 ) ); - sb.append( (char)mMessage.getInt( 186, 192 ) ); - } - - if( blocks >= 3 ) - { - sb.append( (char)mMessage.getInt( - new int[] { 193,194,195,218,219,220,221 } ) ); - sb.append( (char)mMessage.getInt( 222, 228 ) ); - sb.append( (char)mMessage.getInt( 229, 235 ) ); - sb.append( (char)mMessage.getInt( 236, 242 ) ); - sb.append( (char)mMessage.getInt( 243, 249 ) ); - sb.append( (char)mMessage.getInt( 250, 256 ) ); - } + sb.append("SDM:"); - if( blocks == 4 ) - { - sb.append( (char)mMessage.getInt( - new int[] { 257,258,259,278,279,280,281 } ) ); - sb.append( (char)mMessage.getInt( 282, 288 ) ); - sb.append( (char)mMessage.getInt( 289, 295 ) ); - sb.append( (char)mMessage.getInt( 296, 302 ) ); - sb.append( (char)mMessage.getInt( 303, 309 ) ); - sb.append( (char)mMessage.getInt( 310, 316 ) ); - sb.append( (char)mMessage.getInt( 317, 323 ) ); - } + MPTMessageType type = getMessageType(); - return sb.toString(); - } + switch(type) + { + case HEAD_PLUS1: + case HEAD_PLUS2: + case HEAD_PLUS3: + case HEAD_PLUS4: + SDMFormat format = getSDMFormat(); - public String getSDMBCD( MPTMessageType type ) - { - StringBuilder sb = new StringBuilder(); - - int blocks = 0; - - switch( type ) - { - case HEAD_PLUS1: - blocks = 1; - break; - case HEAD_PLUS2: - blocks = 2; - break; - case HEAD_PLUS3: - blocks = 3; - break; - case HEAD_PLUS4: - blocks = 4; - break; - default: - break; - } - - if( blocks >= 1 ) - { - sb.append( getBCD( mMessage.getInt( 90, 93 ) ) ); - sb.append( getBCD( mMessage.getInt( 94, 97 ) ) ); - sb.append( getBCD( mMessage.getInt( 98, 101 ) ) ); - sb.append( getBCD( mMessage.getInt( 102, 105 ) ) ); - sb.append( getBCD( mMessage.getInt( 106, 109 ) ) ); - sb.append( getBCD( mMessage.getInt( 110, 113 ) ) ); - sb.append( getBCD( mMessage.getInt( 114, 117 ) ) ); - sb.append( getBCD( mMessage.getInt( 118, 121 ) ) ); - sb.append( getBCD( mMessage.getInt( 122, 125 ) ) ); - sb.append( getBCD( mMessage.getInt( 126, 129 ) ) ); - } - - if( blocks >= 2 ) - { - sb.append( getBCD( mMessage.getInt( - new int[] { 130,131,150,151 } ) ) ); - sb.append( getBCD( mMessage.getInt( 152, 155 ) ) ); - sb.append( getBCD( mMessage.getInt( 156, 159 ) ) ); - sb.append( getBCD( mMessage.getInt( 160, 163 ) ) ); - sb.append( getBCD( mMessage.getInt( 164, 167 ) ) ); - sb.append( getBCD( mMessage.getInt( 168, 171 ) ) ); - sb.append( getBCD( mMessage.getInt( 172, 175 ) ) ); - sb.append( getBCD( mMessage.getInt( 176, 179 ) ) ); - sb.append( getBCD( mMessage.getInt( 180, 183 ) ) ); - sb.append( getBCD( mMessage.getInt( 184, 187 ) ) ); - sb.append( getBCD( mMessage.getInt( 188, 191 ) ) ); - sb.append( getBCD( mMessage.getInt( 192, 195 ) ) ); - } - - if( blocks >= 3 ) - { - sb.append( getBCD( mMessage.getInt( 218, 221 ) ) ); - sb.append( getBCD( mMessage.getInt( 222, 225 ) ) ); - sb.append( getBCD( mMessage.getInt( 226, 229 ) ) ); - sb.append( getBCD( mMessage.getInt( 230, 233 ) ) ); - sb.append( getBCD( mMessage.getInt( 234, 237 ) ) ); - sb.append( getBCD( mMessage.getInt( 238, 241 ) ) ); - sb.append( getBCD( mMessage.getInt( 242, 245 ) ) ); - sb.append( getBCD( mMessage.getInt( 246, 249 ) ) ); - sb.append( getBCD( mMessage.getInt( 250, 253 ) ) ); - sb.append( getBCD( mMessage.getInt( 254, 257 ) ) ); - } + switch(format) + { + case MPT1327: + sb.append("MPT1327 "); + sb.append(getSDMBinary(type, format)); + break; + case MPT1343_BINARY: + sb.append("BINARY "); + sb.append(getSDMBinary(type, format)); + break; + case MPT1343_COMMAND: + sb.append("COMMAND "); + sb.append(getSDMBinary(type, format)); + break; + case MPT1343_MAP27: + sb.append("MAP27 "); + sb.append(getSDMBinary(type, format)); + break; + case MPT1343_RESERVED: + sb.append("RESERVED "); + sb.append(getSDMBinary(type, format)); + break; + case MPT1343_SPARE: + sb.append("SPARE "); + sb.append(getSDMBinary(type, format)); + break; + case MPT1343_ASCII: + sb.append("ASCII "); + sb.append(getSDMASCII(type)); + break; + case MPT1343_BCD: + sb.append("BCD "); + sb.append(getSDMBCD(type)); + break; + case MPT1343_TELEX: + sb.append("TELEX "); + sb.append(getSDMTelex(type)); + break; + case UNKNOWN: + default: + sb.append("UNKNOWN "); + break; + } - if( blocks == 4 ) - { - sb.append( getBCD( mMessage.getInt( - new int[] { 258,259,278,279 } ) ) ); - sb.append( getBCD( mMessage.getInt( 280, 283 ) ) ); - sb.append( getBCD( mMessage.getInt( 284, 287 ) ) ); - sb.append( getBCD( mMessage.getInt( 288, 291 ) ) ); - sb.append( getBCD( mMessage.getInt( 292, 295 ) ) ); - sb.append( getBCD( mMessage.getInt( 296, 299 ) ) ); - sb.append( getBCD( mMessage.getInt( 300, 303 ) ) ); - sb.append( getBCD( mMessage.getInt( 304, 307 ) ) ); - sb.append( getBCD( mMessage.getInt( 308, 311 ) ) ); - sb.append( getBCD( mMessage.getInt( 312, 315 ) ) ); - sb.append( getBCD( mMessage.getInt( 316, 319 ) ) ); - sb.append( getBCD( mMessage.getInt( 320, 232 ) ) ); - } + break; + case SAMO: + sb.append("SAMO "); + break; + case SAMIS: + sb.append("SAMIS "); + break; + case SAMIU: + sb.append("SAMIU "); + break; + default: + break; - return sb.toString(); - } - - private String getBCD( int value ) - { - if( 0 <= value && value <= 12 ) - { - return BCD[ value ]; - } - else - { - return "?"; - } - } + } - public String getSDMTelex( MPTMessageType type ) - { - ArrayList values = new ArrayList(); - - int blocks = 0; - - switch( type ) - { - case HEAD_PLUS1: - blocks = 1; - break; - case HEAD_PLUS2: - blocks = 2; - break; - case HEAD_PLUS3: - blocks = 3; - break; - case HEAD_PLUS4: - blocks = 4; - break; - default: - break; - } - - if( blocks >= 1 ) - { - values.add( mMessage.getInt( 91, 95 ) ); - values.add( mMessage.getInt( 96, 100 ) ); - values.add( mMessage.getInt( 101, 105 ) ); - values.add( mMessage.getInt( 106, 110 ) ); - values.add( mMessage.getInt( 111, 115 ) ); - values.add( mMessage.getInt( 116, 120 ) ); - values.add( mMessage.getInt( 121, 125 ) ); - values.add( mMessage.getInt( 126, 130 ) ); - } - - if( blocks >= 2 ) - { - values.add( mMessage.getInt( - new int[] { 131,150,151,152,153 } ) ); - values.add( mMessage.getInt( 154, 158 ) ); - values.add( mMessage.getInt( 159, 163 ) ); - values.add( mMessage.getInt( 164, 168 ) ); - values.add( mMessage.getInt( 169, 173 ) ); - values.add( mMessage.getInt( 174, 178 ) ); - values.add( mMessage.getInt( 179, 183 ) ); - values.add( mMessage.getInt( 184, 188 ) ); - values.add( mMessage.getInt( 189, 193 ) ); - } - - if( blocks >= 3 ) - { - values.add( mMessage.getInt( - new int[] { 194,195,218,219,220 } ) ); - values.add( mMessage.getInt( 221, 225 ) ); - values.add( mMessage.getInt( 226, 230 ) ); - values.add( mMessage.getInt( 231, 235 ) ); - values.add( mMessage.getInt( 236, 240 ) ); - values.add( mMessage.getInt( 241, 245 ) ); - values.add( mMessage.getInt( 246, 250 ) ); - } + return sb.toString(); + } - if( blocks == 4 ) - { - values.add( mMessage.getInt( - new int[] { 251,252,253,254,278 } ) ); - values.add( mMessage.getInt( 279, 283 ) ); - values.add( mMessage.getInt( 284, 288 ) ); - values.add( mMessage.getInt( 289, 293 ) ); - values.add( mMessage.getInt( 294, 298 ) ); - values.add( mMessage.getInt( 299, 303 ) ); - values.add( mMessage.getInt( 304, 308 ) ); - values.add( mMessage.getInt( 309, 313 ) ); - values.add( mMessage.getInt( 314, 318 ) ); - values.add( mMessage.getInt( 319, 323 ) ); - } + public String getSDMBinary(MPTMessageType type, SDMFormat format) + { + StringBuilder sb = new StringBuilder(); - StringBuilder sb = new StringBuilder(); + switch(type) + { + case HEAD_PLUS1: + if(format == SDMFormat.MPT1327) + { + sb.append(mMessage.getHex(B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12)); + } + else + { + sb.append(mMessage.getHex(B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12)); + } + break; + case HEAD_PLUS2: + if(format == SDMFormat.MPT1327) + { + sb.append(mMessage.getHex(B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B3_SDM_START, + B3_SDM_END, + 12)); + } + else + { + sb.append(mMessage.getHex(B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B3_SDM_START, + B3_SDM_END, + 12)); + } + break; + case HEAD_PLUS3: + if(format == SDMFormat.MPT1327) + { + sb.append(mMessage.getHex(B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B3_SDM_START, + B3_SDM_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B4_SDM_STF0_START, + B4_SDM_STF0_END, + 12)); + } + else + { + sb.append(mMessage.getHex(B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B3_SDM_START, + B3_SDM_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B4_SDM_STF1_START, + B4_SDM_STF1_END, + 12)); + } + break; + case HEAD_PLUS4: + if(format == SDMFormat.MPT1327) + { + sb.append(mMessage.getHex(B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B3_SDM_START, + B3_SDM_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B4_SDM_STF0_START, + B4_SDM_STF0_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B5_SDM_START, + B5_SDM_END, + 12)); + } + else + { + sb.append(mMessage.getHex(B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B3_SDM_START, + B3_SDM_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B4_SDM_STF1_START, + B4_SDM_STF1_END, + 12)); + sb.append(" "); + sb.append(mMessage.getHex(B5_SDM_START, + B5_SDM_END, + 12)); + } + break; + default: + break; + } - /* Decode the numeric values. 28 and 29 are used to shift the decoder - * to use the figures alphabet or the letters alphabet */ - boolean figure = false; - - for( Integer value: values ) - { - if( 0 <= value && value < 32 ) - { - if( value == 28 ) - { - figure = false; - } - else if( value == 29 ) - { - figure = true; - } - else - { - if( figure ) - { - sb.append( TELEX_FIGURES[ value ] ); - } - else - { - sb.append( TELEX_LETTERS[ value ] ); - } - } - } - } - - return sb.toString(); - } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } + return sb.toString(); + } - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - return aliases; - } - - - /** - * SDM Message format ICDs - */ - public enum SDMFormat - { - MPT1327, - MPT1343_BINARY, - MPT1343_BCD, - MPT1343_TELEX, - MPT1343_ASCII, - MPT1343_RESERVED, - MPT1343_SPARE, - MPT1343_COMMAND, - MPT1343_MAP27, - UNKNOWN; - - public static SDMFormat valueOf( boolean stf, int gfi ) - { - if( stf ) //MPT1343 - { - switch( gfi ) - { - case 0: - return MPT1343_BINARY; - case 1: - return MPT1343_BCD; - case 2: - return MPT1343_TELEX; - case 3: - return MPT1343_ASCII; - case 4: - return MPT1343_RESERVED; - case 5: - return MPT1343_SPARE; - case 6: - return MPT1343_COMMAND; - case 7: - return MPT1343_MAP27; - default: - return UNKNOWN; - } - } - else - { - return MPT1327; - } - } - } - - - public enum IdentType - { - ALLI( "SYSTEM-WIDE" ), - DIVERTI( "DIVERT" ), - DNI( "DATA NETWORK GATEWAY" ), - DUMMYI( "DUMMY IDENT" ), - INCI( "INCLUDE IN CALL" ), - IPFIXI( "INTER-PREFIX" ), - PABXI( "PABX GATEWAY" ), - PSTNGI( "PSTN GATEWAY" ), - PSTNSI1( "PSTN OR NETWORK 1" ), - PSTNSI2( "PSTN OR NETWORK 2" ), - PSTNSI3( "PSTN OR NETWORK 3" ), - PSTNSI4( "PSTN OR NETWORK 4" ), - PSTNSI5( "PSTN OR NETWORK 5" ), - PSTNSI6( "PSTN OR NETWORK 6" ), - PSTNSI7( "PSTN OR NETWORK 7" ), - PSTNSI8( "PSTN OR NETWORK 8" ), - PSTNSI9( "PSTN OR NETWORK 9" ), - PSTNSI10( "PSTN OR NETWORK 10" ), - PSTNSI11( "PSTN OR NETWORK 11" ), - PSTNSI12( "PSTN OR NETWORK 12" ), - PSTNSI13( "PSTN OR NETWORK 13" ), - PSTNSI14( "PSTN OR NETWORK 14" ), - PSTNSI15( "PSTN OR NETWORK 15" ), - REGI( "REGISTRATION" ), - RESERVED( "RESERVED" ), - SDMI( "SHORT DATA MESSAGE" ), - SPARE( "SPARE" ), - TSCI( "SYSTEM CONTROLLER" ), - USER( "COMMON-PREFIX IDENT" ), - UNKNOWN( "UNKNOWN" ); - - private String mLabel; - - private IdentType( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static IdentType fromIdent( int ident ) - { - switch( ident ) - { - case 0: - return DUMMYI; - case 8101: - return PSTNGI; - case 8102: - return PABXI; - case 8103: - return DNI; - case 8121: - return PSTNSI1; - case 8122: - return PSTNSI2; - case 8123: - return PSTNSI3; - case 8124: - return PSTNSI4; - case 8125: - return PSTNSI5; - case 8126: - return PSTNSI6; - case 8127: - return PSTNSI7; - case 8128: - return PSTNSI8; - case 8129: - return PSTNSI9; - case 8130: - return PSTNSI10; - case 8131: - return PSTNSI11; - case 8132: - return PSTNSI12; - case 8133: - return PSTNSI13; - case 8134: - return PSTNSI14; - case 8135: - return PSTNSI15; - case 8181: - case 8182: - case 8183: - case 8184: - return RESERVED; - case 8185: - return REGI; - case 8186: - return INCI; - case 8187: - return DIVERTI; - case 8188: - return SDMI; - case 8189: - return IPFIXI; - case 8190: - return TSCI; - case 8191: - return ALLI; - default: - if( 1 <= ident && ident <= 8100 ) - { - return USER; - } - else if( ( 8104 <= ident && ident <= 8120 ) || - ( 8136 <= ident && ident <= 8180 ) ) - { - return SPARE; - } - else - { - return UNKNOWN; - } - } - } - } - - public enum Descriptor - { - DESC0( "EXTENDED ADDRESSING INFORMATION", - "ESN", - "SINGLE SEGMENT TRANSACTIONS (SST) ONLY" ), - DESC1( "PSTN DIALED DIGITS", "RESERVED", "N/A" ), - DESC2( "PABX EXTENSION", "RESERVED", "N/A" ), - DESC3( "N/A", "N/A", "N/A" ), - DESC4( "N/A", "N/A", "FIRST MST SEGMENT OR SINGLE SST" ), - DESC5( "N/A", "N/A", "SECOND MST SEGMENT OR SINGLE SST" ), - DESC6( "N/A", "N/A", "THIRD MST SEGMENT OR SINGLE SST" ), - DESC7( "RESERVED", "RESERVED", "FOURTH MST SEGMENT OR SINGLE SST" ), - UNKNOWN( "UNKNOWN", "UNKNOWN", "UNKNOWN" ); - - private String mMode1Label; - private String mMode2Label; - private String mSDMLabel; - - private Descriptor( String mode1Label, String mode2Label, String sdmLabel ) - { - mMode1Label = mode1Label; - mMode2Label = mode2Label; - mSDMLabel = sdmLabel; - } - - /* Label that applies to AHYC Mode 1 responses */ - public String getMode1Label() - { - return mMode1Label; - } + public String getSDMASCII(MPTMessageType type) + { + StringBuilder sb = new StringBuilder(); - /* Label that applies to AHYC Mode 2 responses */ - public String getMode2Label() - { - return mMode2Label; - } + int blocks = 0; - /* Label that applies to Short Data Messages (SDM) */ - public String getSDMLabel() - { - return mSDMLabel; - } - - public static Descriptor fromNumber( int number ) - { - switch( number ) - { - case 0: - return DESC0; - case 1: - return DESC1; - case 2: - return DESC2; - case 3: - return DESC3; - case 4: - return DESC4; - case 5: - return DESC5; - case 6: - return DESC6; - case 7: - return DESC7; - default: - return UNKNOWN; - } - } - } - - /** - * Indicates the number of data codeword slots appended to a HEAD message - */ - public enum Slots - { - SLOTS_0( "RESERVED" ), - SLOTS_1( "ADDRESS CODEWORD ONLY" ), - SLOTS_2( "ADDRESS CODEWORD & 1-2 DATA CODEWORDS" ), - SLOTS_3( "ADDRESS CODEWORD & 3-4 DATA CODEWORDS" ), - UNKNOWN( "UNKNOWN" ); + switch(type) + { + case HEAD_PLUS1: + blocks = 1; + break; + case HEAD_PLUS2: + blocks = 2; + break; + case HEAD_PLUS3: + blocks = 3; + break; + case HEAD_PLUS4: + blocks = 4; + break; + default: + break; + } - private String mLabel; - - private Slots( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static Slots fromNumber( int number ) - { - switch( number ) - { - case 0: - return SLOTS_0; - case 1: - return SLOTS_1; - case 2: - return SLOTS_2; - case 3: - return SLOTS_3; - default: - return UNKNOWN; - } - } - } - + if(blocks >= 1) + { + sb.append((char)mMessage.getInt(91, 97)); + sb.append((char)mMessage.getInt(98, 104)); + sb.append((char)mMessage.getInt(105, 111)); + sb.append((char)mMessage.getInt(112, 118)); + sb.append((char)mMessage.getInt(119, 125)); + } - public enum SystemDefinition - { - UNKNOWN( "UNKNOWN" ), - ANNOUNCE_CONTROL_CHANNEL( "ANNOUNCE CONTROL CHANNEL" ), - WITHDRAW_CONTROL_CHANNEL( "WITHDRAW CONTROL CHANNEL" ), - SPECIFY_CALL_MAINTENANCE_PARAMETERS( "CALL MAINT PARAMETERS" ), - SPECIFY_REGISTRATION_PARAMETERS( "REGISTRATION PARAMETERS" ), - BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER( "NEIGHBOR" ), - VOTE_NOW_ADVICE( "VOTE NOW" ); + if(blocks >= 2) + { + sb.append((char)mMessage.getInt( + new int[]{126, 127, 128, 129, 130, 131, 150})); + sb.append((char)mMessage.getInt(151, 157)); + sb.append((char)mMessage.getInt(158, 164)); + sb.append((char)mMessage.getInt(165, 171)); + sb.append((char)mMessage.getInt(172, 178)); + sb.append((char)mMessage.getInt(179, 185)); + sb.append((char)mMessage.getInt(186, 192)); + } - private String mLabel; - - private SystemDefinition( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static SystemDefinition fromNumber( int number ) - { - switch( number ) - { - case 0: - return ANNOUNCE_CONTROL_CHANNEL; - case 1: - return WITHDRAW_CONTROL_CHANNEL; - case 2: - return SPECIFY_CALL_MAINTENANCE_PARAMETERS; - case 3: - return SPECIFY_REGISTRATION_PARAMETERS; - case 4: - return BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER; - case 5: - return VOTE_NOW_ADVICE; - default: - return UNKNOWN; - } - } - } - - public enum MPTMessageType - { - UNKN( "Unknown" ), - GTC( "GTC - Goto Channel" ), //0 - ALH( "ALH - Aloha" ), //256 - ALHS( "ALHS - Standard Data Excluded" ), //257 - ALHD( "ALHD - Simple Calls Excluded" ), //258 - ALHE( "ALHE - Emergency Calls Only" ), //259 - ALHR( "ALHR - Emergency or Registration" ), //260 - ALHX( "ALHX - Registration Excluded" ), //261 - ALHF( "ALHF - Fallback Mode" ), //262 - //263 - ACK( "ACK - Acknowledge" ), //264 - ACKI( "ACKI - More To Follow" ), //265 - ACKQ( "ACKQ - Call Queued" ), //266 - ACKX( "ACKX - Message Rejected" ), //267 - ACKV( "ACKV - Called Unit Unavailable" ), //268 - ACKE( "ACKE - Emergency" ), //269 - ACKT( "ACKT - Try On Given Address" ), //270 - ACKB( "ACKB - Call Back/Negative Ack" ), //271 - AHOY( "AHOY - General Availability Check" ), //272 - AHYX( "AHYX - Cancel Alert/Waiting Status" ), //274 - AHYP( "AHYP - Called Unit Presence Monitoring" ),//277 - AHYQ( "AHYQ - Status Message" ), //278 - AHYC( "AHYC - Short Data Message" ), //279 - MARK( "MARK - Control Channel Marker" ), //280 - MAINT( "MAINT - Call Maintenance Message"), //281 - CLEAR( "CLEAR - Down From Allocated Channel" ), //282 - MOVE( "MOVE - To Specified Channel" ), //283 - BCAST( "BCAST - System Parameters" ), //284 - //285 - 287 - SAMO( "SAMO - Outbound Single Address" ), //288 - 303 - SAMIS( "SAMIS - Inbound Solicited Single Address" ), //288 - 295 - SAMIU( "SAMIU - Inbound Unsolicited Single Address" ), //296 - 303 - HEAD_PLUS1( "HEAD - 1 DATA CODEWORD" ), //304 - 307 - HEAD_PLUS2( "HEAD - 2 DATA CODEWORDS" ), //308 - 311 - HEAD_PLUS3( "HEAD - 3 DATA CODEWORDS" ), //312 - 315 - HEAD_PLUS4( "HEAD - 4 DATA CODEWORDS" ), //316 - 319 - GTT( "GTT - Go To Transaction" ), //320 - 335 - SACK( "SACK - Standard Data Selective Ack Header" ), //416 - 423 - DACK_DAL( "DACK - Data Ack + DAL" ), //416 - DACK_DALG( "DACK - Data Ack + DALG" ), //417 - DACK_DALN( "DACK - Data Ack + DALN" ), //418 - DACK_GO( "DACK - GO Fragment Transmit Invitation" ), //419 - DACKZ( "DACKZ - Data Ack For Expedited Data"), //420 - DACKD( "DACKD - Data Ack For Standard Data" ), //421 - DAHY( "DAHY - Standard Data Ahoy" ), //424 - DRQG( "DRQG - Repeat Group Message" ), //426 - DRQZ( "DRQZ - Request Containing Expedited Data" ), //428 - DAHYZ( "DAHYZ - Expedited Data Ahoy" ), //428 - DAHYX( "DAHYX - Standard Data For Closing TRANS" ),//430 - DRQX( "DRQX - Request To Close A Transaction" ), //430 - RLA( "RLA - Repeat Last ACK" ), //431 - SITH( "SITH - Standard Data Address Codeword Data Item" ); //440 - 443 - - private String mDescription; - - private MPTMessageType( String description ) - { - mDescription = description; - } - - public String getDescription() - { - return mDescription; - } - - public String toString() - { - return getDescription(); - } + if(blocks >= 3) + { + sb.append((char)mMessage.getInt( + new int[]{193, 194, 195, 218, 219, 220, 221})); + sb.append((char)mMessage.getInt(222, 228)); + sb.append((char)mMessage.getInt(229, 235)); + sb.append((char)mMessage.getInt(236, 242)); + sb.append((char)mMessage.getInt(243, 249)); + sb.append((char)mMessage.getInt(250, 256)); + } - public static MPTMessageType fromNumber( int number ) - { - if( number < 256 ) - { - return GTC; - } - - switch( number ) - { - case 256: - return ALH; - case 257: - return ALHS; - case 258: - return ALHD; - case 259: - return ALHE; - case 260: - return ALHR; - case 261: - return ALHX; - case 262: - return ALHF; - case 264: - return ACK; - case 265: - return ACKI; - case 266: - return ACKQ; - case 267: - return ACKX; - case 268: - return ACKV; - case 269: - return ACKE; - case 270: - return ACKT; - case 271: - return ACKB; - case 272: - return AHOY; - case 274: - return AHYX; - case 277: - return AHYP; - case 278: - return AHYQ; - case 279: - return AHYC; - case 280: - return MARK; - case 281: - return MAINT; - case 282: - return CLEAR; - case 283: - return MOVE; - case 284: - return BCAST; - case 288: - case 289: - case 290: - case 291: - case 292: - case 293: - case 294: - case 295: - case 296: - case 297: - case 298: - case 299: - case 300: - case 301: - case 302: - case 303: - return SAMO; - case 304: - case 305: - case 306: - case 307: - return HEAD_PLUS1; - case 308: - case 309: - case 310: - case 311: - return HEAD_PLUS2; - case 312: - case 313: - case 314: - case 315: - return HEAD_PLUS3; - case 316: - case 317: - case 318: - case 319: - return HEAD_PLUS4; - case 320: - case 321: - case 322: - case 323: - case 324: - case 325: - case 326: - case 327: - case 328: - case 329: - case 330: - case 331: - case 332: - case 333: - case 334: - case 335: - return GTT; - case 416: - return DACK_DAL; - case 417: - return DACK_DALG; - case 418: - return DACK_DALN; - case 419: - return DACK_GO; - case 420: - return DACKZ; - case 421: - return DACKD; - case 424: - return DAHY; - case 426: - return DRQG; - case 428: - return DAHYZ; - case 430: - return DAHYX; - case 440: - case 441: - case 442: - case 443: - return SITH; - default: - return UNKN; - } - } - } + if(blocks == 4) + { + sb.append((char)mMessage.getInt( + new int[]{257, 258, 259, 278, 279, 280, 281})); + sb.append((char)mMessage.getInt(282, 288)); + sb.append((char)mMessage.getInt(289, 295)); + sb.append((char)mMessage.getInt(296, 302)); + sb.append((char)mMessage.getInt(303, 309)); + sb.append((char)mMessage.getInt(310, 316)); + sb.append((char)mMessage.getInt(317, 323)); + } + + return sb.toString(); + } + + public String getSDMBCD(MPTMessageType type) + { + StringBuilder sb = new StringBuilder(); + + int blocks = 0; + + switch(type) + { + case HEAD_PLUS1: + blocks = 1; + break; + case HEAD_PLUS2: + blocks = 2; + break; + case HEAD_PLUS3: + blocks = 3; + break; + case HEAD_PLUS4: + blocks = 4; + break; + default: + break; + } + + if(blocks >= 1) + { + sb.append(getBCD(mMessage.getInt(90, 93))); + sb.append(getBCD(mMessage.getInt(94, 97))); + sb.append(getBCD(mMessage.getInt(98, 101))); + sb.append(getBCD(mMessage.getInt(102, 105))); + sb.append(getBCD(mMessage.getInt(106, 109))); + sb.append(getBCD(mMessage.getInt(110, 113))); + sb.append(getBCD(mMessage.getInt(114, 117))); + sb.append(getBCD(mMessage.getInt(118, 121))); + sb.append(getBCD(mMessage.getInt(122, 125))); + sb.append(getBCD(mMessage.getInt(126, 129))); + } + + if(blocks >= 2) + { + sb.append(getBCD(mMessage.getInt( + new int[]{130, 131, 150, 151}))); + sb.append(getBCD(mMessage.getInt(152, 155))); + sb.append(getBCD(mMessage.getInt(156, 159))); + sb.append(getBCD(mMessage.getInt(160, 163))); + sb.append(getBCD(mMessage.getInt(164, 167))); + sb.append(getBCD(mMessage.getInt(168, 171))); + sb.append(getBCD(mMessage.getInt(172, 175))); + sb.append(getBCD(mMessage.getInt(176, 179))); + sb.append(getBCD(mMessage.getInt(180, 183))); + sb.append(getBCD(mMessage.getInt(184, 187))); + sb.append(getBCD(mMessage.getInt(188, 191))); + sb.append(getBCD(mMessage.getInt(192, 195))); + } + + if(blocks >= 3) + { + sb.append(getBCD(mMessage.getInt(218, 221))); + sb.append(getBCD(mMessage.getInt(222, 225))); + sb.append(getBCD(mMessage.getInt(226, 229))); + sb.append(getBCD(mMessage.getInt(230, 233))); + sb.append(getBCD(mMessage.getInt(234, 237))); + sb.append(getBCD(mMessage.getInt(238, 241))); + sb.append(getBCD(mMessage.getInt(242, 245))); + sb.append(getBCD(mMessage.getInt(246, 249))); + sb.append(getBCD(mMessage.getInt(250, 253))); + sb.append(getBCD(mMessage.getInt(254, 257))); + } + + if(blocks == 4) + { + sb.append(getBCD(mMessage.getInt( + new int[]{258, 259, 278, 279}))); + sb.append(getBCD(mMessage.getInt(280, 283))); + sb.append(getBCD(mMessage.getInt(284, 287))); + sb.append(getBCD(mMessage.getInt(288, 291))); + sb.append(getBCD(mMessage.getInt(292, 295))); + sb.append(getBCD(mMessage.getInt(296, 299))); + sb.append(getBCD(mMessage.getInt(300, 303))); + sb.append(getBCD(mMessage.getInt(304, 307))); + sb.append(getBCD(mMessage.getInt(308, 311))); + sb.append(getBCD(mMessage.getInt(312, 315))); + sb.append(getBCD(mMessage.getInt(316, 319))); + sb.append(getBCD(mMessage.getInt(320, 232))); + } + + return sb.toString(); + } + + private String getBCD(int value) + { + if(0 <= value && value <= 12) + { + return BCD[value]; + } + else + { + return "?"; + } + } + + public String getSDMTelex(MPTMessageType type) + { + ArrayList values = new ArrayList(); + + int blocks = 0; + + switch(type) + { + case HEAD_PLUS1: + blocks = 1; + break; + case HEAD_PLUS2: + blocks = 2; + break; + case HEAD_PLUS3: + blocks = 3; + break; + case HEAD_PLUS4: + blocks = 4; + break; + default: + break; + } + + if(blocks >= 1) + { + values.add(mMessage.getInt(91, 95)); + values.add(mMessage.getInt(96, 100)); + values.add(mMessage.getInt(101, 105)); + values.add(mMessage.getInt(106, 110)); + values.add(mMessage.getInt(111, 115)); + values.add(mMessage.getInt(116, 120)); + values.add(mMessage.getInt(121, 125)); + values.add(mMessage.getInt(126, 130)); + } + + if(blocks >= 2) + { + values.add(mMessage.getInt( + new int[]{131, 150, 151, 152, 153})); + values.add(mMessage.getInt(154, 158)); + values.add(mMessage.getInt(159, 163)); + values.add(mMessage.getInt(164, 168)); + values.add(mMessage.getInt(169, 173)); + values.add(mMessage.getInt(174, 178)); + values.add(mMessage.getInt(179, 183)); + values.add(mMessage.getInt(184, 188)); + values.add(mMessage.getInt(189, 193)); + } + + if(blocks >= 3) + { + values.add(mMessage.getInt( + new int[]{194, 195, 218, 219, 220})); + values.add(mMessage.getInt(221, 225)); + values.add(mMessage.getInt(226, 230)); + values.add(mMessage.getInt(231, 235)); + values.add(mMessage.getInt(236, 240)); + values.add(mMessage.getInt(241, 245)); + values.add(mMessage.getInt(246, 250)); + } + + if(blocks == 4) + { + values.add(mMessage.getInt( + new int[]{251, 252, 253, 254, 278})); + values.add(mMessage.getInt(279, 283)); + values.add(mMessage.getInt(284, 288)); + values.add(mMessage.getInt(289, 293)); + values.add(mMessage.getInt(294, 298)); + values.add(mMessage.getInt(299, 303)); + values.add(mMessage.getInt(304, 308)); + values.add(mMessage.getInt(309, 313)); + values.add(mMessage.getInt(314, 318)); + values.add(mMessage.getInt(319, 323)); + } + + StringBuilder sb = new StringBuilder(); + + /* Decode the numeric values. 28 and 29 are used to shift the decoder + * to use the figures alphabet or the letters alphabet */ + boolean figure = false; + + for(Integer value : values) + { + if(0 <= value && value < 32) + { + if(value == 28) + { + figure = false; + } + else if(value == 29) + { + figure = true; + } + else + { + if(figure) + { + sb.append(TELEX_FIGURES[value]); + } + else + { + sb.append(TELEX_LETTERS[value]); + } + } + } + } + + return sb.toString(); + } + + /** + * SDM Message format ICDs + */ + public enum SDMFormat + { + MPT1327, + MPT1343_BINARY, + MPT1343_BCD, + MPT1343_TELEX, + MPT1343_ASCII, + MPT1343_RESERVED, + MPT1343_SPARE, + MPT1343_COMMAND, + MPT1343_MAP27, + UNKNOWN; + + public static SDMFormat valueOf(boolean stf, int gfi) + { + if(stf) //MPT1343 + { + switch(gfi) + { + case 0: + return MPT1343_BINARY; + case 1: + return MPT1343_BCD; + case 2: + return MPT1343_TELEX; + case 3: + return MPT1343_ASCII; + case 4: + return MPT1343_RESERVED; + case 5: + return MPT1343_SPARE; + case 6: + return MPT1343_COMMAND; + case 7: + return MPT1343_MAP27; + default: + return UNKNOWN; + } + } + else + { + return MPT1327; + } + } + } + + + public enum IdentType + { + ALLI("SYSTEM-WIDE"), + DIVERTI("DIVERT"), + DNI("DATA NETWORK GATEWAY"), + DUMMYI("DUMMY IDENT"), + INCI("INCLUDE IN CALL"), + IPFIXI("INTER-PREFIX"), + PABXI("PABX GATEWAY"), + PSTNGI("PSTN GATEWAY"), + PSTNSI1("PSTN OR NETWORK 1"), + PSTNSI2("PSTN OR NETWORK 2"), + PSTNSI3("PSTN OR NETWORK 3"), + PSTNSI4("PSTN OR NETWORK 4"), + PSTNSI5("PSTN OR NETWORK 5"), + PSTNSI6("PSTN OR NETWORK 6"), + PSTNSI7("PSTN OR NETWORK 7"), + PSTNSI8("PSTN OR NETWORK 8"), + PSTNSI9("PSTN OR NETWORK 9"), + PSTNSI10("PSTN OR NETWORK 10"), + PSTNSI11("PSTN OR NETWORK 11"), + PSTNSI12("PSTN OR NETWORK 12"), + PSTNSI13("PSTN OR NETWORK 13"), + PSTNSI14("PSTN OR NETWORK 14"), + PSTNSI15("PSTN OR NETWORK 15"), + REGI("REGISTRATION"), + RESERVED("RESERVED"), + SDMI("SHORT DATA MESSAGE"), + SPARE("SPARE"), + TSCI("SYSTEM CONTROLLER"), + USER("COMMON-PREFIX IDENT"), + UNKNOWN("UNKNOWN"); + + private String mLabel; + + IdentType(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static IdentType fromIdent(int ident) + { + switch(ident) + { + case 0: + return DUMMYI; + case 8101: + return PSTNGI; + case 8102: + return PABXI; + case 8103: + return DNI; + case 8121: + return PSTNSI1; + case 8122: + return PSTNSI2; + case 8123: + return PSTNSI3; + case 8124: + return PSTNSI4; + case 8125: + return PSTNSI5; + case 8126: + return PSTNSI6; + case 8127: + return PSTNSI7; + case 8128: + return PSTNSI8; + case 8129: + return PSTNSI9; + case 8130: + return PSTNSI10; + case 8131: + return PSTNSI11; + case 8132: + return PSTNSI12; + case 8133: + return PSTNSI13; + case 8134: + return PSTNSI14; + case 8135: + return PSTNSI15; + case 8181: + case 8182: + case 8183: + case 8184: + return RESERVED; + case 8185: + return REGI; + case 8186: + return INCI; + case 8187: + return DIVERTI; + case 8188: + return SDMI; + case 8189: + return IPFIXI; + case 8190: + return TSCI; + case 8191: + return ALLI; + default: + if(1 <= ident && ident <= 8100) + { + return USER; + } + else if((8104 <= ident && ident <= 8120) || + (8136 <= ident && ident <= 8180)) + { + return SPARE; + } + else + { + return UNKNOWN; + } + } + } + } + + public enum Descriptor + { + DESC0("EXTENDED ADDRESSING INFORMATION", + "ESN", + "SINGLE SEGMENT TRANSACTIONS (SST) ONLY"), + DESC1("PSTN DIALED DIGITS", "RESERVED", "N/A"), + DESC2("PABX EXTENSION", "RESERVED", "N/A"), + DESC3("N/A", "N/A", "N/A"), + DESC4("N/A", "N/A", "FIRST MST SEGMENT OR SINGLE SST"), + DESC5("N/A", "N/A", "SECOND MST SEGMENT OR SINGLE SST"), + DESC6("N/A", "N/A", "THIRD MST SEGMENT OR SINGLE SST"), + DESC7("RESERVED", "RESERVED", "FOURTH MST SEGMENT OR SINGLE SST"), + UNKNOWN("UNKNOWN", "UNKNOWN", "UNKNOWN"); + + private String mMode1Label; + private String mMode2Label; + private String mSDMLabel; + + Descriptor(String mode1Label, String mode2Label, String sdmLabel) + { + mMode1Label = mode1Label; + mMode2Label = mode2Label; + mSDMLabel = sdmLabel; + } + + /* Label that applies to AHYC Mode 1 responses */ + public String getMode1Label() + { + return mMode1Label; + } + + /* Label that applies to AHYC Mode 2 responses */ + public String getMode2Label() + { + return mMode2Label; + } + + /* Label that applies to Short Data Messages (SDM) */ + public String getSDMLabel() + { + return mSDMLabel; + } + + public static Descriptor fromNumber(int number) + { + switch(number) + { + case 0: + return DESC0; + case 1: + return DESC1; + case 2: + return DESC2; + case 3: + return DESC3; + case 4: + return DESC4; + case 5: + return DESC5; + case 6: + return DESC6; + case 7: + return DESC7; + default: + return UNKNOWN; + } + } + } + + /** + * Indicates the number of data codeword slots appended to a HEAD message + */ + public enum Slots + { + SLOTS_0("RESERVED"), + SLOTS_1("ADDRESS CODEWORD ONLY"), + SLOTS_2("ADDRESS CODEWORD & 1-2 DATA CODEWORDS"), + SLOTS_3("ADDRESS CODEWORD & 3-4 DATA CODEWORDS"), + UNKNOWN("UNKNOWN"); + + private String mLabel; + + Slots(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static Slots fromNumber(int number) + { + switch(number) + { + case 0: + return SLOTS_0; + case 1: + return SLOTS_1; + case 2: + return SLOTS_2; + case 3: + return SLOTS_3; + default: + return UNKNOWN; + } + } + } + + + public enum SystemDefinition + { + UNKNOWN("UNKNOWN"), + ANNOUNCE_CONTROL_CHANNEL("ANNOUNCE CONTROL CHANNEL"), + WITHDRAW_CONTROL_CHANNEL("WITHDRAW CONTROL CHANNEL"), + SPECIFY_CALL_MAINTENANCE_PARAMETERS("CALL MAINT PARAMETERS"), + SPECIFY_REGISTRATION_PARAMETERS("REGISTRATION PARAMETERS"), + BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER("NEIGHBOR"), + VOTE_NOW_ADVICE("VOTE NOW"); + + private String mLabel; + + SystemDefinition(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static SystemDefinition fromNumber(int number) + { + switch(number) + { + case 0: + return ANNOUNCE_CONTROL_CHANNEL; + case 1: + return WITHDRAW_CONTROL_CHANNEL; + case 2: + return SPECIFY_CALL_MAINTENANCE_PARAMETERS; + case 3: + return SPECIFY_REGISTRATION_PARAMETERS; + case 4: + return BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER; + case 5: + return VOTE_NOW_ADVICE; + default: + return UNKNOWN; + } + } + } + + public enum MPTMessageType + { + UNKN("Unknown"), + GTC("GTC - Goto Channel"), //0 + ALH("ALH - Aloha"), //256 + ALHS("ALHS - Standard Data Excluded"), //257 + ALHD("ALHD - Simple Calls Excluded"), //258 + ALHE("ALHE - Emergency Calls Only"), //259 + ALHR("ALHR - Emergency or Registration"), //260 + ALHX("ALHX - Registration Excluded"), //261 + ALHF("ALHF - Fallback Mode"), //262 + //263 + ACK("ACK - Acknowledge"), //264 + ACKI("ACKI - More To Follow"), //265 + ACKQ("ACKQ - Call Queued"), //266 + ACKX("ACKX - Message Rejected"), //267 + ACKV("ACKV - Called Unit Unavailable"), //268 + ACKE("ACKE - Emergency"), //269 + ACKT("ACKT - Try On Given Address"), //270 + ACKB("ACKB - Call Back/Negative Ack"), //271 + AHOY("AHOY - General Availability Check"), //272 + AHYX("AHYX - Cancel Alert/Waiting Status"), //274 + AHYP("AHYP - Called Unit Presence Monitoring"),//277 + AHYQ("AHYQ - Status Message"), //278 + AHYC("AHYC - Short Data Message"), //279 + MARK("MARK - Control Channel Marker"), //280 + MAINT("MAINT - Call Maintenance Message"), //281 + CLEAR("CLEAR - Down From Allocated Channel"), //282 + MOVE("MOVE - To Specified Channel"), //283 + BCAST("BCAST - System Parameters"), //284 + //285 - 287 + SAMO("SAMO - Outbound Single Address"), //288 - 303 + SAMIS("SAMIS - Inbound Solicited Single Address"), //288 - 295 + SAMIU("SAMIU - Inbound Unsolicited Single Address"), //296 - 303 + HEAD_PLUS1("HEAD - 1 DATA CODEWORD"), //304 - 307 + HEAD_PLUS2("HEAD - 2 DATA CODEWORDS"), //308 - 311 + HEAD_PLUS3("HEAD - 3 DATA CODEWORDS"), //312 - 315 + HEAD_PLUS4("HEAD - 4 DATA CODEWORDS"), //316 - 319 + GTT("GTT - Go To Transaction"), //320 - 335 + SACK("SACK - Standard Data Selective Ack Header"), //416 - 423 + DACK_DAL("DACK - Data Ack + DAL"), //416 + DACK_DALG("DACK - Data Ack + DALG"), //417 + DACK_DALN("DACK - Data Ack + DALN"), //418 + DACK_GO("DACK - GO Fragment Transmit Invitation"), //419 + DACKZ("DACKZ - Data Ack For Expedited Data"), //420 + DACKD("DACKD - Data Ack For Standard Data"), //421 + DAHY("DAHY - Standard Data Ahoy"), //424 + DRQG("DRQG - Repeat Group Message"), //426 + DRQZ("DRQZ - Request Containing Expedited Data"), //428 + DAHYZ("DAHYZ - Expedited Data Ahoy"), //428 + DAHYX("DAHYX - Standard Data For Closing TRANS"),//430 + DRQX("DRQX - Request To Close A Transaction"), //430 + RLA("RLA - Repeat Last ACK"), //431 + SITH("SITH - Standard Data Address Codeword Data Item"); //440 - 443 + + private String mDescription; + + MPTMessageType(String description) + { + mDescription = description; + } + + public String getDescription() + { + return mDescription; + } + + public String toString() + { + return getDescription(); + } + + public static MPTMessageType fromNumber(int number) + { + if(number < 256) + { + return GTC; + } + + switch(number) + { + case 256: + return ALH; + case 257: + return ALHS; + case 258: + return ALHD; + case 259: + return ALHE; + case 260: + return ALHR; + case 261: + return ALHX; + case 262: + return ALHF; + case 264: + return ACK; + case 265: + return ACKI; + case 266: + return ACKQ; + case 267: + return ACKX; + case 268: + return ACKV; + case 269: + return ACKE; + case 270: + return ACKT; + case 271: + return ACKB; + case 272: + return AHOY; + case 274: + return AHYX; + case 277: + return AHYP; + case 278: + return AHYQ; + case 279: + return AHYC; + case 280: + return MARK; + case 281: + return MAINT; + case 282: + return CLEAR; + case 283: + return MOVE; + case 284: + return BCAST; + case 288: + case 289: + case 290: + case 291: + case 292: + case 293: + case 294: + case 295: + case 296: + case 297: + case 298: + case 299: + case 300: + case 301: + case 302: + case 303: + return SAMO; + case 304: + case 305: + case 306: + case 307: + return HEAD_PLUS1; + case 308: + case 309: + case 310: + case 311: + return HEAD_PLUS2; + case 312: + case 313: + case 314: + case 315: + return HEAD_PLUS3; + case 316: + case 317: + case 318: + case 319: + return HEAD_PLUS4; + case 320: + case 321: + case 322: + case 323: + case 324: + case 325: + case 326: + case 327: + case 328: + case 329: + case 330: + case 331: + case 332: + case 333: + case 334: + case 335: + return GTT; + case 416: + return DACK_DAL; + case 417: + return DACK_DALG; + case 418: + return DACK_DALN; + case 419: + return DACK_GO; + case 420: + return DACKZ; + case 421: + return DACKD; + case 424: + return DAHY; + case 426: + return DRQG; + case 428: + return DAHYZ; + case 430: + return DAHYX; + case 440: + case 441: + case 442: + case 443: + return SITH; + default: + return UNKN; + } + } + } + + public MPT1327Talkgroup getFromIdentifier() + { + if(mFromIdentifier == null) + { + mFromIdentifier = MPT1327Talkgroup.createFrom(getPrefix(), getIdent2()); + } + + return mFromIdentifier; + } + + public MPT1327Talkgroup getToIdentifier() + { + if(mToIdentifier == null) + { + mToIdentifier = MPT1327Talkgroup.createTo(getPrefix(), getIdent1()); + } + + return mToIdentifier; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getToIdentifier()); + mIdentifiers.add(getFromIdentifier()); + } + + return mIdentifiers; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java index 21f0c9f4..c5f2c2c0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java @@ -1,62 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.mpt1327; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.module.decode.mpt1327.MPT1327Message.MPTMessageType; +import io.github.dsheirer.message.IMessage; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; -public class MPT1327MessageFilter extends Filter +public class MPT1327MessageFilter extends Filter { - private HashMap> mFilterElements = - new HashMap>(); + private Map> mFilterElements = new HashMap<>(); - public MPT1327MessageFilter() - { - super( "MPT1327 Message Type Filter" ); - - for( MPTMessageType type: MPT1327Message.MPTMessageType.values() ) - { - if( type != MPTMessageType.UNKN ) - { - mFilterElements.put( type, - new FilterElement( type ) ); - } - } - } - - @Override - public boolean passes( Message message ) + public MPT1327MessageFilter() { - if( mEnabled && canProcess( message ) ) - { - MPT1327Message mpt = (MPT1327Message)message; + super("MPT1327 Message Type Filter"); + + for(MPT1327Message.MPTMessageType type : MPT1327Message.MPTMessageType.values()) + { + if(type != MPT1327Message.MPTMessageType.UNKN) + { + mFilterElements.put(type, new FilterElement(type)); + } + } + } + + @Override + public boolean passes(IMessage message) + { + if(mEnabled && canProcess(message)) + { + MPT1327Message mpt = (MPT1327Message) message; + + FilterElement element = + mFilterElements.get(mpt.getMessageType()); + + if(element != null) + { + return element.isEnabled(); + } + } - FilterElement element = - mFilterElements.get( mpt.getMessageType() ); - - if( element != null ) - { - return element.isEnabled(); - } - } - return false; } - @Override - public boolean canProcess( Message message ) + @Override + public boolean canProcess(IMessage message) { return message instanceof MPT1327Message; } - @Override + @Override public List> getFilterElements() { - return new ArrayList>( mFilterElements.values() ); + return new ArrayList>(mFilterElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageProcessor.java index d8a145e3..67737e4e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageProcessor.java @@ -1,65 +1,61 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.mpt1327; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class MPT1327MessageProcessor implements Listener +public class MPT1327MessageProcessor implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( MPT1327MessageProcessor.class ); + private final static Logger mLog = LoggerFactory.getLogger(MPT1327MessageProcessor.class); - private Listener mMessageListener; - - private AliasList mAliasList; - - public MPT1327MessageProcessor( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mMessageListener = null; - } - - @Override - public void receive( BinaryMessage buffer ) + private Listener mMessageListener; + + public MPT1327MessageProcessor() { - if( mMessageListener != null ) - { - MPT1327Message message = new MPT1327Message( buffer, mAliasList ); - - mMessageListener.receive( message ); - } } - - public void setMessageListener( Listener listener ) + + public void dispose() { - mMessageListener = listener; + mMessageListener = null; + } + + @Override + public void receive(CorrectedBinaryMessage message) + { + if(mMessageListener != null) + { + mMessageListener.receive(new MPT1327Message(message)); + } + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java new file mode 100644 index 00000000..661ada65 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java @@ -0,0 +1,390 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.mpt1327; + +import io.github.dsheirer.controller.channel.Channel; +import io.github.dsheirer.controller.channel.ChannelEvent; +import io.github.dsheirer.controller.channel.ChannelGrantEvent; +import io.github.dsheirer.controller.channel.IChannelEventListener; +import io.github.dsheirer.controller.channel.IChannelEventProvider; +import io.github.dsheirer.controller.channel.map.ChannelMap; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.module.Module; +import io.github.dsheirer.module.decode.config.DecodeConfiguration; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEventProvider; +import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.source.config.SourceConfigTuner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class MPT1327TrafficChannelManager extends Module implements IDecodeEventProvider, IChannelEventListener, + IChannelEventProvider +{ + private final static Logger mLog = LoggerFactory.getLogger(MPT1327TrafficChannelManager.class); + + public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED"; + public static final String MAX_TRAFFIC_CHANNELS_EXCEEDED = "MAX TRAFFIC CHANNELS EXCEEDED"; + + private Queue mAvailableTrafficChannelQueue = new ConcurrentLinkedQueue<>(); + private List mManagedTrafficChannels; + private Map mAllocatedTrafficChannelMap = new ConcurrentHashMap<>(); + private Map mChannelGrantEventMap = new ConcurrentHashMap<>(); + private TrafficChannelTeardownMonitor mTrafficChannelTeardownMonitor = new TrafficChannelTeardownMonitor(); + private Listener mChannelEventListener; + private Listener mDecodeEventListener; + private ChannelMap mChannelMap; + + /** + * Constructs an MPT1327 traffic channel manage. + * + * @param parentChannel containing configuration items for traffic channels to inherit + */ + public MPT1327TrafficChannelManager(Channel parentChannel, ChannelMap channelMap) + { + createTrafficChannels(parentChannel); + mChannelMap = channelMap; + } + + /** + * Processes channel grants to allocate traffic channels and track overall channel usage. Generates + * decode events for each new channel that is allocated. + */ + public void processChannelGrant(MPT1327Message mpt1327Message) + { + if(mpt1327Message.getMessageType() == MPT1327Message.MPTMessageType.GTC) + { + IdentifierCollection identifierCollection = new IdentifierCollection(mpt1327Message.getIdentifiers()); + MPT1327Channel mpt1327Channel = MPT1327Channel.create(mpt1327Message.getChannel()); + mpt1327Channel.setChannelMap(mChannelMap); + + MPT1327ChannelGrantEvent event = mChannelGrantEventMap.get(mpt1327Channel); + + if(event != null) + { + if(isSameTalkgroup(identifierCollection, event.getIdentifierCollection())) + { + //update the ending timestamp so that the duration value is correctly calculated + event.end(mpt1327Message.getTimestamp()); + return; + } + else if(mAllocatedTrafficChannelMap.containsKey(mpt1327Channel)) + { + Channel trafficChannel = mAllocatedTrafficChannelMap.get(mpt1327Channel); + broadcast(new ChannelEvent(trafficChannel, ChannelEvent.Event.REQUEST_DISABLE)); + } + } + + MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp()) + .channel(mpt1327Channel) + .eventDescription("Call") + .details("Traffic Channel Grant") + .identifiers(identifierCollection) + .build(); + + mChannelGrantEventMap.put(mpt1327Channel, channelGrantEvent); + + if(mpt1327Channel.getDownlinkFrequency() == 0) + { + channelGrantEvent.setDetails("Invalid Channel Map - No Frequency For Channel " + mpt1327Channel.getChannelNumber()); + } + else + { + Channel trafficChannel = mAvailableTrafficChannelQueue.poll(); + + if(trafficChannel == null) + { + channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED); + channelGrantEvent.setEventDescription("Detect:" + channelGrantEvent.getEventDescription()); + return; + } + + SourceConfigTuner sourceConfig = new SourceConfigTuner(); + sourceConfig.setFrequency(mpt1327Channel.getDownlinkFrequency()); + trafficChannel.setSourceConfiguration(sourceConfig); + mAllocatedTrafficChannelMap.put(mpt1327Channel, trafficChannel); + broadcast(new ChannelGrantEvent(trafficChannel, ChannelEvent.Event.REQUEST_ENABLE, mpt1327Channel, identifierCollection)); + } + + broadcast(channelGrantEvent); + } + } + + + /** + * Creates up to the maximum number of traffic channels for use in allocating traffic channels. + * + * @param parentChannel for inheriting naming, alias list and traffic channel pool size properties + */ + private void createTrafficChannels(Channel parentChannel) + { + DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration(); + List trafficChannelList = new ArrayList<>(); + + if(decodeConfiguration instanceof DecodeConfigMPT1327) + { + DecodeConfigMPT1327 decodeConfigMPT1327 = (DecodeConfigMPT1327)decodeConfiguration; + + int maxTrafficChannels = decodeConfigMPT1327.getTrafficChannelPoolSize(); + + for(int x = 0; x < maxTrafficChannels; x++) + { + Channel trafficChannel = new Channel("TRAFFIC", Channel.ChannelType.TRAFFIC); + trafficChannel.setAliasListName(parentChannel.getAliasListName()); + trafficChannel.setSystem(parentChannel.getSystem()); + trafficChannel.setSite(parentChannel.getSite()); + trafficChannel.setDecodeConfiguration(decodeConfigMPT1327); + trafficChannel.setEventLogConfiguration(parentChannel.getEventLogConfiguration()); + trafficChannel.setRecordConfiguration(parentChannel.getRecordConfiguration()); + trafficChannelList.add(trafficChannel); + } + } + + mAvailableTrafficChannelQueue.addAll(trafficChannelList); + mManagedTrafficChannels = Collections.unmodifiableList(trafficChannelList); + } + + /** + * Broadcasts an initial or update decode event to any registered listener. + */ + public void broadcast(DecodeEvent decodeEvent) + { + if(mDecodeEventListener != null) + { + mDecodeEventListener.receive(decodeEvent); + } + } + + /** + * Channel event listener to receive notifications that a traffic channel has ended processing and we + * can reclaim the traffic channel for reuse. + * + * @return listener for processing channel events. + */ + @Override + public Listener getChannelEventListener() + { + return mTrafficChannelTeardownMonitor; + } + + /** + * Broadcasts a channel event to a registered external listener (for action). + */ + private void broadcast(ChannelEvent channelEvent) + { + if(mChannelEventListener != null) + { + mChannelEventListener.receive(channelEvent); + } + } + + /** + * Sets the external channel event listener to receive channel events from this traffic channel manager + */ + @Override + public void setChannelEventListener(Listener listener) + { + mChannelEventListener = listener; + } + + /** + * Removes the channel event listener + */ + @Override + public void removeChannelEventListener() + { + mChannelEventListener = null; + } + + @Override + public void reset() + { + + } + + @Override + public void start() + { + + } + + @Override + public void stop() + { + List channels = new ArrayList<>(mAllocatedTrafficChannelMap.values()); + + //Issue a disable request for each traffic channel + for(Channel channel : channels) + { + mLog.debug("Stopping traffic channel: " + channel); + broadcast(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_DISABLE)); + } + } + + + /** + * Compares the TO role identifier(s) from each collection for equality + * + * @param collection1 containing a TO identifier + * @param collection2 containing a TO identifier + * @return true if both collections contain a TO identifier and the TO identifiers are the same value + */ + private boolean isSameTalkgroup(IdentifierCollection collection1, IdentifierCollection collection2) + { + Identifier toIdentifier1 = getToIdentifier(collection1); + Identifier toIdentifier2 = getToIdentifier(collection2); + return Objects.equals(toIdentifier1, toIdentifier2); + } + + /** + * Retrieves the first identifier with a TO role. + * + * @param collection containing a TO identifier + * @return TO identifier or null + */ + private Identifier getToIdentifier(IdentifierCollection collection) + { + List identifiers = collection.getIdentifiers(Role.TO); + + if(identifiers.size() >= 1) + { + return identifiers.get(0); + } + + return null; + } + + @Override + public void dispose() + { + for(Channel trafficChannel : mAvailableTrafficChannelQueue) + { + broadcast(new ChannelEvent(trafficChannel, ChannelEvent.Event.REQUEST_DISABLE)); + } + + mAvailableTrafficChannelQueue.clear(); + } + + + @Override + public void addDecodeEventListener(Listener listener) + { + mDecodeEventListener = listener; + } + + @Override + public void removeDecodeEventListener(Listener listener) + { + mDecodeEventListener = null; + } + + /** + * Monitors channel teardown events to detect when traffic channel processing has ended. Reclaims the + * channel instance for reuse by future traffic channel grants. + */ + public class TrafficChannelTeardownMonitor implements Listener + { + @Override + public synchronized void receive(ChannelEvent channelEvent) + { + Channel channel = channelEvent.getChannel(); + + if(channel.isTrafficChannel() && mManagedTrafficChannels.contains(channel)) + { + switch(channelEvent.getEvent()) + { + case NOTIFICATION_PROCESSING_STOP: + MPT1327Channel toRemove = null; + + for(Map.Entry entry : mAllocatedTrafficChannelMap.entrySet()) + { + if(entry.getValue() == channel) + { + toRemove = entry.getKey(); + continue; + } + } + + if(toRemove != null) + { + mAllocatedTrafficChannelMap.remove(toRemove); + mAvailableTrafficChannelQueue.add(channel); + + MPT1327ChannelGrantEvent event = mChannelGrantEventMap.get(toRemove); + + if(event != null) + { + event.end(System.currentTimeMillis()); + broadcast(event); + } + } + break; + case NOTIFICATION_PROCESSING_START_REJECTED: + MPT1327Channel rejected = null; + + for(Map.Entry entry : mAllocatedTrafficChannelMap.entrySet()) + { + if(entry.getValue() == channel) + { + rejected = entry.getKey(); + continue; + } + } + + if(rejected != null) + { + mAllocatedTrafficChannelMap.remove(rejected); + mAvailableTrafficChannelQueue.add(channel); + + MPT1327ChannelGrantEvent event = mChannelGrantEventMap.get(rejected); + + if(event != null) + { + if(channelEvent.getDescription() != null) + { + event.setDetails(channelEvent.getDescription() + " - " + event.getDetails()); + } + else + { + event.setDetails(channelEvent.getDescription() + " - " + CHANNEL_START_REJECTED); + } + broadcast(event); + } + } + break; + } + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/channel/MPT1327Channel.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/channel/MPT1327Channel.java new file mode 100644 index 00000000..36ac3c44 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/channel/MPT1327Channel.java @@ -0,0 +1,142 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.mpt1327.channel; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.controller.channel.map.ChannelMap; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.protocol.Protocol; + +import java.util.Objects; + +/** + * MPT-1327 Channel + */ +public class MPT1327Channel implements IChannelDescriptor +{ + private int mChannelNumber; + private ChannelMap mChannelMap; + + /** + * Constructs a channel instance + */ + public MPT1327Channel(int channelNumber) + { + mChannelNumber = channelNumber; + } + + public String toString() + { + return String.valueOf(mChannelNumber); + } + + /** + * Channel number for this channel. + */ + public int getChannelNumber() + { + return mChannelNumber; + } + + /** + * Assigns a channel map to this channel so that the frequency can be calculated. + */ + public void setChannelMap(ChannelMap channelMap) + { + mChannelMap = channelMap; + } + + @Override + public long getDownlinkFrequency() + { + if(mChannelMap != null) + { + return mChannelMap.getFrequency(mChannelNumber); + } + + return 0; + } + + @Override + public long getUplinkFrequency() + { + return 0; + } + + @Override + public int[] getFrequencyBandIdentifiers() + { + return new int[0]; + } + + @Override + public void setFrequencyBand(IFrequencyBand bandIdentifier) + { + //no-op + } + + @Override + public boolean isTDMAChannel() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 0; + } + + @Override + public Protocol getProtocol() + { + return Protocol.MPT1327; + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + MPT1327Channel that = (MPT1327Channel)o; + return getChannelNumber() == that.getChannelNumber(); + } + + @Override + public int hashCode() + { + return Objects.hash(getChannelNumber()); + } + + /** + * Creates a new MPT1327 channel + */ + public static MPT1327Channel create(int channelNumber) + { + return new MPT1327Channel(channelNumber); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/IdentType.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/IdentType.java new file mode 100644 index 00000000..56a60205 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/IdentType.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.module.decode.mpt1327.identifier; + +/** + * MPT-1327 Ident Types enumeration + */ +public enum IdentType +{ + ALLI_ALL_CALL, + CUSTOM_IDENT, + DIVERTI_DIVERT_IDENT, + DNI_DATA_NETWORK_GATEWAY_IDENT, + DUMMI, + INCI_INCLUDE_IDENT, + IPFIXI_INTER_PREFIX_IDENT, + PABXI_PABX_GATEWAY_IDENT, + PSTNGI_PSTN_GATEWAY_IDENT, + PSTNSI_AND_NETSI_SHORT_FORM_IDENT, + REGI_REGISTRATION_IDENT, + RESERVED, + SDMI_SHORT_DATA_MESSAGE, + TSC_TRUNKING_SYSTEM_CONTROLLER, + USER_IDENT, + UNKNOWN; + +// User idents 1 - 8100(individual and group idents) + + public static IdentType fromIdent(int ident) + { + if(1 <= ident && ident <= 8100) + { + return USER_IDENT; + } + + switch(ident) + { + case 0: + return DUMMI; + case 8101: + return PSTNGI_PSTN_GATEWAY_IDENT; + case 8102: + return PABXI_PABX_GATEWAY_IDENT; + case 8103: + return DNI_DATA_NETWORK_GATEWAY_IDENT; + case 8185: + return REGI_REGISTRATION_IDENT; + case 8186: + return INCI_INCLUDE_IDENT; + case 8187: + return DIVERTI_DIVERT_IDENT; + case 8188: + return SDMI_SHORT_DATA_MESSAGE; + case 8189: + return IPFIXI_INTER_PREFIX_IDENT; + case 8190: + return TSC_TRUNKING_SYSTEM_CONTROLLER; + case 8191: + return ALLI_ALL_CALL; + } + + if(8136 <= ident && ident <= 8180) + { + return CUSTOM_IDENT; + } + else if(8121 <= ident && ident <= 8135) + { + return PSTNSI_AND_NETSI_SHORT_FORM_IDENT; + } + else if((8181 <= ident && ident <= 8184) || (8104 <= ident && ident <= 8120)) + { + return RESERVED; + } + + return UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327SiteIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327SiteIdentifier.java new file mode 100644 index 00000000..3f861765 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327SiteIdentifier.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.module.decode.mpt1327.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class MPT1327SiteIdentifier extends IntegerIdentifier +{ + public MPT1327SiteIdentifier(int site) + { + super(site, IdentifierClass.NETWORK, Form.SITE, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.MPT1327; + } + + public static MPT1327SiteIdentifier create(int site) + { + return new MPT1327SiteIdentifier(site); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327Talkgroup.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327Talkgroup.java new file mode 100644 index 00000000..4294112b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/identifier/MPT1327Talkgroup.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ + +package io.github.dsheirer.module.decode.mpt1327.identifier; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * MPT-1327 Talkgroup Identifier. + * + * Note: the isGroup() method of the talkgroup identifier parent class does not apply here since we cannot determine + * if a talkgroup is a group or a unit identifier. + */ +public class MPT1327Talkgroup extends TalkgroupIdentifier +{ + public MPT1327Talkgroup(Integer value, Role role) + { + super(value, role, false); + } + + @Override + public Protocol getProtocol() + { + return Protocol.MPT1327; + } + + /** + * This identifier formatted as PPP-IIII where P=prefix and I=ident + */ + public String formatted() + { + return String.format("%03d-%04d", getPrefix(), getIdent()); + } + + /** + * Prefix for the talkgroup + */ + public int getPrefix() + { + return (getValue() >> 13) & 0x7F; + } + + /** + * Ident for the talkgroup + */ + public int getIdent() + { + return getValue() & 0x1FFF; + } + + /** + * Indicates the type of ident value + */ + public IdentType getIdentType() + { + return IdentType.fromIdent(getIdent()); + } + + /** + * Creates a FROM role talkgroup identifier + */ + public static MPT1327Talkgroup createFrom(int prefix, int ident) + { + if(0 < ident && ident <= 8100) + { + return new MPT1327Talkgroup(((prefix << 13) + ident), Role.FROM); + } + else + { + //Mask the prefix for ident values of 0, 8101-8192 + return new MPT1327Talkgroup(ident, Role.FROM); + } + } + + /** + * Creates a TO role talkgroup identifier + */ + public static MPT1327Talkgroup createTo(int prefix, int ident) + { + if(0 < ident && ident <= 8100) + { + return new MPT1327Talkgroup(((prefix << 13) + ident), Role.TO); + } + else + { + //Mask the prefix for ident values of 0, 8101-8192 + return new MPT1327Talkgroup(ident, Role.TO); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/IDataUnitDetectListener.java b/src/main/java/io/github/dsheirer/module/decode/p25/IDataUnitDetectListener.java new file mode 100644 index 00000000..1af23a80 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/IDataUnitDetectListener.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +/** + * Listener interface to be notified each time a P25 sync pattern and data unit has been detected + * and the data unit is correct after error detection and correction and/or when the sync has been lost. + */ +public interface IDataUnitDetectListener +{ + /** + * Indicates that a P25 sync has been detected and a P25 data unit was successfully decoded. + * @param dataUnitID that was contained in the detected NID + * @param nac or Network Access Code that was contained in the detected NID + * @param discardedDibits prior to detecting the P25 sync pattern + * @param bitErrors detected and corrected from both the sync pattern and the NID. + * @param correctedNid bits corrected by the BCH error correction code (temporary until message parsers are updated + */ + void dataUnitDetected(DataUnitID dataUnitID, int nac, int bitErrors, int discardedDibits, int[] correctedNid); + + /** + * Indicates that sync has been lost on the dibit stream + * + * @param bitsProcessed since the last sync detect + */ + void syncLost(int bitsProcessed); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25CallEvent.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25CallEvent.java deleted file mode 100644 index 99d80555..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25CallEvent.java +++ /dev/null @@ -1,170 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.p25; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class P25CallEvent extends CallEvent -{ - private String mChannel; - private long mFrequency; - - public P25CallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String channel, - long frequency, - String details ) - { - super( DecoderType.P25_PHASE1, callEventType, - aliasList, fromID, toID, details ); - - mChannel = channel; - mFrequency = frequency; - } - - private Alias getAlias( String talkgroup ) - { - if( hasAliasList() ) - { - return getAliasList().getTalkgroupAlias( talkgroup ); - } - - return null; - } - - @Override - public Alias getFromIDAlias() - { - return getAlias( getFromID() ); - } - - @Override - public Alias getToIDAlias() - { - return getAlias( getToID() ); - } - - @Override - public String getChannel() - { - return mChannel; - } - - @Override - public long getFrequency() - { - return mFrequency; - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "P25 " ); - sb.append( getCallEventType().name() ); - sb.append( " CHAN[" ); - sb.append( getChannel() ); - sb.append( "] TO[" ); - sb.append( getToID() ); - sb.append( "] FROM[" ); - sb.append( getFromID() ); - - return sb.toString(); - } - - public static class Builder - { - /* Required parameters */ - private CallEventType mCallEventType; - - /* Optional parameters */ - private AliasList mAliasList; - private String mFromID; - private String mToID; - private String mDetails; - private String mChannel; - private long mFrequency; - - public Builder( CallEventType callEventType ) - { - mCallEventType = callEventType; - } - - public Builder aliasList( AliasList aliasList ) - { - mAliasList = aliasList; - return this; - } - - public Builder from( String val ) - { - mFromID = val; - return this; - } - - public Builder channel( String channel ) - { - mChannel = channel; - return this; - } - - public Builder details( String details ) - { - mDetails = details; - return this; - } - - public Builder frequency( long frequency ) - { - mFrequency = frequency; - return this; - } - - public Builder to( String toID ) - { - mToID = toID; - return this; - } - - public P25CallEvent build() - { - return new P25CallEvent( this ); - } - - } - - /** - * Private constructor for the builder - */ - private P25CallEvent( Builder builder ) - { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mChannel, - builder.mFrequency, - builder.mDetails ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java new file mode 100644 index 00000000..377fdcd2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java @@ -0,0 +1,172 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.module.decode.p25.reference.ServiceOptions; +import io.github.dsheirer.protocol.Protocol; + +public class P25ChannelGrantEvent extends P25DecodeEvent +{ + private ServiceOptions mServiceOptions; + + public P25ChannelGrantEvent(long timestamp) + { + super(timestamp); + } + + /** + * Creates a new decode event builder with the specified start timestamp. + * @param timeStart for the event + * @return builder + */ + public static P25ChannelGrantDecodeEventBuilder builder(long timeStart, ServiceOptions serviceOptions) + { + return new P25ChannelGrantDecodeEventBuilder(timeStart, serviceOptions); + } + + /** + * Service options for the channel grant + */ + public ServiceOptions getServiceOptions() + { + return mServiceOptions; + } + + + /** + * Service options for the channel grant + */ + public void setServiceOptions(ServiceOptions serviceOptions) + { + mServiceOptions = serviceOptions; + } + + /** + * Indicates if this channel grant event has non-null service options + */ + public boolean hasServiceOptions() + { + return mServiceOptions != null; + } + + /** + * Builder pattern for constructing decode events. + */ + public static class P25ChannelGrantDecodeEventBuilder + { + protected long mTimeStart; + protected long mDuration; + protected String mEventDescription; + protected IdentifierCollection mIdentifierCollection; + protected IChannelDescriptor mChannelDescriptor; + protected String mDetails; + private ServiceOptions mServiceOptions; + + /** + * Constructs a builder instance with the specified start time in milliseconds + * + * @param timeStart + */ + public P25ChannelGrantDecodeEventBuilder(long timeStart, ServiceOptions serviceOptions) + { + mTimeStart = timeStart; + mServiceOptions = serviceOptions; + } + + /** + * Sets the duration value + * @param duration in milliseconds + */ + public P25ChannelGrantDecodeEventBuilder duration(long duration) + { + mDuration = duration; + return this; + } + + /** + * Sets the duration value using the end - start timestamps + * @param timestamp for end of event + */ + public P25ChannelGrantDecodeEventBuilder end(long timestamp) + { + mDuration = timestamp - mTimeStart; + return this; + } + + /** + * Sets the channel descriptor for this event + * @param channelDescriptor + */ + public P25ChannelGrantDecodeEventBuilder channel(IChannelDescriptor channelDescriptor) + { + mChannelDescriptor = channelDescriptor; + return this; + } + + /** + * Sets the event description text + * @param description of the event + */ + public P25ChannelGrantDecodeEventBuilder eventDescription(String description) + { + mEventDescription = description; + return this; + } + + /** + * Sets the identifier collection. + * @param identifierCollection containing optional identifiers like TO, FROM, frequency and + * alias list configuration name. + */ + public P25ChannelGrantDecodeEventBuilder identifiers(IdentifierCollection identifierCollection) + { + mIdentifierCollection = identifierCollection; + return this; + } + + /** + * Sets the details for the event + * @param details + */ + public P25ChannelGrantDecodeEventBuilder details(String details) + { + mDetails = details; + return this; + } + + /** + * Builds the decode event + */ + public P25ChannelGrantEvent build() + { + P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mTimeStart); + decodeEvent.setProtocol(Protocol.APCO25); + decodeEvent.setChannelDescriptor(mChannelDescriptor); + decodeEvent.setDetails(mDetails); + decodeEvent.setDuration(mDuration); + decodeEvent.setEventDescription(mEventDescription); + decodeEvent.setIdentifierCollection(mIdentifierCollection); + decodeEvent.setServiceOptions(mServiceOptions); + return decodeEvent; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelStatusProcessor.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelStatusProcessor.java new file mode 100644 index 00000000..8fdfb8fa --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelStatusProcessor.java @@ -0,0 +1,85 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.dsp.symbol.Dibit; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.sample.Listener; + +/** + * Processes P25 status dibits to determine if the channel being monitored is the output of a repeater or if + * the channel is from a mobile subscriber. + * + * Dibit 00 is used by the subscriber + * Dibits 01 and 11 are used by the repeater. + * Dibit 10 is used by both and is ignored by this processor. + */ +public class P25ChannelStatusProcessor implements Listener +{ + private int mSubscriberCount = 0; + private int mRepeaterCount = 0; + private Direction mDirection = Direction.OUTBOUND; + + public void receive(Dibit status) + { + switch(status) + { + case D00_PLUS_1: + mSubscriberCount++; + break; + case D01_PLUS_3: + case D11_MINUS_3: + mRepeaterCount++; + break; + } + + update(); + } + + private void update() + { + if(mRepeaterCount > mSubscriberCount) + { + mDirection = Direction.OUTBOUND; + + if(mRepeaterCount == Integer.MAX_VALUE) + { + mRepeaterCount = 1000; + mSubscriberCount = 0; + } + } + else + { + mDirection = Direction.INBOUND; + + if(mSubscriberCount == Integer.MAX_VALUE) + { + mSubscriberCount = 1000; + mRepeaterCount = 0; + } + } + } + + public Direction getDirection() + { + return mDirection; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DataUnitDetector.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DataUnitDetector.java new file mode 100644 index 00000000..3a2e59ec --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DataUnitDetector.java @@ -0,0 +1,557 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25; + +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.edac.BCH_63_16_11; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.sample.Listener; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class P25DataUnitDetector implements Listener, ISyncDetectListener +{ + private final static Logger mLog = LoggerFactory.getLogger(P25DataUnitDetector.class); + private static final int DATA_UNIT_DIBIT_LENGTH = 57; //56 dibits plus 1 status symbol + private static final int SYNC_DIBIT_LENGTH = 24; + private static final int MAXIMUM_SYNC_MATCH_BIT_ERRORS = 9; + private P25SyncDetector mSyncDetector; + private NIDDelayBuffer mDataUnitBuffer = new NIDDelayBuffer(); + private DibitDelayBuffer mSyncDelayBuffer = new DibitDelayBuffer(DATA_UNIT_DIBIT_LENGTH - SYNC_DIBIT_LENGTH); + private IDataUnitDetectListener mDataUnitDetectListener; + private boolean mInitialSyncTestProcessed = false; + private int mDibitsProcessed = 0; + private BCH_63_16_11 mNIDDecoder = new BCH_63_16_11(); + private DataUnitID mPreviousDataUnitId = DataUnitID.TERMINATOR_DATA_UNIT; + private int mNIDDetectionCount; + + public P25DataUnitDetector(IDataUnitDetectListener dataUnitDetectListener, IPhaseLockedLoop phaseLockedLoop) + { + mDataUnitDetectListener = dataUnitDetectListener; + mSyncDetector = new P25SyncDetector(this, phaseLockedLoop); + } + + /** + * Sets the sample rate for the phase inversion sync detector + */ + public void setSampleRate(double sampleRate) + { + mSyncDetector.setSampleRate(sampleRate); + } + + public void reset() + { + mDibitsProcessed = 0; + mInitialSyncTestProcessed = false; + } + + @Override + public void syncDetected(int bitErrors) + { + mInitialSyncTestProcessed = true; + checkForNid(bitErrors, false); + } + + @Override + public void syncLost() + { + dispatchSyncLoss(0); + } + + private void dispatchSyncLoss(int bitsProcessed) + { + if(mDataUnitDetectListener != null) + { + mDataUnitDetectListener.syncLost(bitsProcessed); + } + } + + @Override + public void receive(Dibit dibit) + { + mDibitsProcessed++; + + //Broadcast a sync loss every 4800 dibits/9600 bits ... or 1x per second for phase 1 + if(mDibitsProcessed > 4864) + { + dispatchSyncLoss(9600); + mDibitsProcessed -= 4800; + } + + mDataUnitBuffer.put(dibit); + + //Feed the sync detect with a 32 dibit delay from the data unit buffer so that if/when + //a sync detect occurs, the data unit buffer is already filled with both the sync dibits + //and the NID dibits and we can test for a valid NID + mSyncDetector.receive(mSyncDelayBuffer.getAndPut(dibit)); + + //If the sync detector doesn't fire and we've processed enough dibits for a sync/nid sequence + //immediately following a valid message, then test for a NID anyway ... maybe the sync was corrupted + if(!mInitialSyncTestProcessed && mDibitsProcessed == DATA_UNIT_DIBIT_LENGTH) + { + mInitialSyncTestProcessed = true; + checkForNid(mSyncDetector.getPrimarySyncMatchErrorCount(), true); + } + } + + /** + * Chects/tests the contents of the data unit buffer for a valid NID when a sync pattern is detected + * or when commanded following a valid message sequence + * + * @param bitErrorCount when comparing the sync pattern to the received bit sequence + * @param forcedCheck indicates if the NID check was forced, meaning that the primary sync detector + * did not detect a sync, however we expect there to be a message sync following a successfully + * decoded and framed preceeding messsage. In these instances, the framer will allow for a less + * than perfect match on the sync pattern, as long as the NID passes error check. + */ + private void checkForNid(int bitErrorCount, boolean forcedCheck) + { + if(bitErrorCount <= MAXIMUM_SYNC_MATCH_BIT_ERRORS) + { + int[] nid = mSyncDelayBuffer.getNID(); + int[] correctedNid = new int[63]; + + //If decoder indicates there are no unrecoverable errors .... + if(!mNIDDecoder.decode(nid, correctedNid)) + { + mNIDDetectionCount++; + + int nidBitErrorCount = getBitErrorCount(nid, correctedNid); + + if(mDataUnitDetectListener != null) + { + mPreviousDataUnitId = getDataUnitID(correctedNid); + + mDataUnitDetectListener.dataUnitDetected(mPreviousDataUnitId, getNAC(correctedNid), + (bitErrorCount + nidBitErrorCount), (mDibitsProcessed - DATA_UNIT_DIBIT_LENGTH), correctedNid); + } + } + else if(mPreviousDataUnitId == DataUnitID.LOGICAL_LINK_DATA_UNIT_1) + { + //We have a good sync match, but the NID didn't pass error control and we're in the middle + //of voice call, so treat this message as voice message, but set the previous duid to + //terminator so we can end if there isn't a subsequent voice message + mDataUnitDetectListener.dataUnitDetected(DataUnitID.LOGICAL_LINK_DATA_UNIT_2, -1, + (bitErrorCount + 64), (mDibitsProcessed - DATA_UNIT_DIBIT_LENGTH), new int[63]); + + mPreviousDataUnitId = DataUnitID.TERMINATOR_DATA_UNIT; + } + else if(mPreviousDataUnitId == DataUnitID.LOGICAL_LINK_DATA_UNIT_2) + { + //We have a good sync match, but the NID didn't pass error control and we're in the middle + //of voice call, so treat this message as voice message, but set the previous duid to + //terminator so we can end if there isn't a subsequent voice message + mDataUnitDetectListener.dataUnitDetected(DataUnitID.LOGICAL_LINK_DATA_UNIT_1, -1, + (bitErrorCount + 64), (mDibitsProcessed - DATA_UNIT_DIBIT_LENGTH), new int[63]); + + mPreviousDataUnitId = DataUnitID.TERMINATOR_DATA_UNIT; + } + } + } + + /** + * Determines the data unit ID present in the nid value. + * @param nid in reverse bit order + * @return + */ + public DataUnitID getDataUnitID(int[] nid) + { + int duid = 0; + + if(nid[47] == 1) + { + duid ^= 1; + } + + if(nid[48] == 1) + { + duid ^= 2; + } + + if(nid[49] == 1) + { + duid ^= 4; + } + + if(nid[50] == 1) + { + duid ^= 8; + } + + return DataUnitID.fromValue(duid); + } + + /** + * Determines the Network Access Code (NAC) present in the nid value. + * @param nid in reverse bit order + * @return nac + */ + public int getNAC(int[] nid) + { + int nac = 0; + + if(nid[51] == 1) + { + nac ^= 1; + } + + if(nid[52] == 1) + { + nac ^= 2; + } + + if(nid[53] == 1) + { + nac ^= 4; + } + + if(nid[54] == 1) + { + nac ^= 8; + } + + if(nid[55] == 1) + { + nac ^= 16; + } + + if(nid[56] == 1) + { + nac ^= 32; + } + + if(nid[57] == 1) + { + nac ^= 64; + } + + if(nid[58] == 1) + { + nac ^= 128; + } + + if(nid[59] == 1) + { + nac ^= 256; + } + + if(nid[60] == 1) + { + nac ^= 512; + } + + if(nid[61] == 1) + { + nac ^= 1024; + } + + if(nid[62] == 1) + { + nac ^= 2048; + } + + return nac; + } + + public int getNIDDetectionCount() + { + return mNIDDetectionCount; + } + + public class NIDDelayBuffer extends DibitDelayBuffer + { + public NIDDelayBuffer() + { + super(DATA_UNIT_DIBIT_LENGTH); + } + + public int[] getNID() + { + int[] nid = new int[64]; + + int nidPointer = 0; + int bufferPointer = mPointer; + + while(nidPointer < 64) + { + if(mBuffer[bufferPointer].getBit2()) + { + nid[nidPointer] = 1; + } + + nidPointer++; + + if(mBuffer[bufferPointer].getBit1()) + { + nid[nidPointer] = 1; + } + + nidPointer++; + + bufferPointer--; + + if(bufferPointer < 0) + { + bufferPointer += mBuffer.length; + } + + //Check for the status symbol + if(nidPointer == 70) + { + nidPointer += 2; + } + } + + return nid; + } + } + + + /** + * Circular buffer for storing and accessing dibits. + */ + public class DibitDelayBuffer + { + protected Dibit[] mBuffer; + protected int mPointer; + + /** + * Constructs a dibit delay buffer of the specified length + */ + public DibitDelayBuffer(int length) + { + mBuffer = new Dibit[length]; + + //Preload the buffer to avoid null pointers + for(int x = 0; x < length; x++) + { + mBuffer[x] = Dibit.D00_PLUS_1; + } + } + + /** + * Fetches the NID in reverse order format required for the ECC code + */ + public int[] getNID() + { + int[] nid = new int[63]; + + int nidPointer = 0; + int bufferPointer = mPointer - 1; + + if(bufferPointer < 0) + { + bufferPointer += mBuffer.length; + } + + //Skip bit 2 of the starting (ie last) dibit ... we only want 63 of the 64 bits from the dibit buffer + if(mBuffer[bufferPointer].getBit1()) + { + nid[nidPointer] = 1; + } + + nidPointer++; + + bufferPointer--; + + if(bufferPointer < 0) + { + bufferPointer += mBuffer.length; + } + + while(nidPointer < 63) + { + if(mBuffer[bufferPointer].getBit2()) + { + nid[nidPointer] = 1; + } + + nidPointer++; + + if(mBuffer[bufferPointer].getBit1()) + { + nid[nidPointer] = 1; + } + + nidPointer++; + + bufferPointer--; + + //Check for and skip the status symbol that gets inserted after 70 bits, meaning + //it will be located in the buffer after 22 NID bits + if(nidPointer == 41) + { + bufferPointer--; + } + + if(bufferPointer < 0) + { + bufferPointer += mBuffer.length; + } + } + + return nid; + } + + public void log() + { + StringBuilder sb = new StringBuilder(); + + int counter = 0; + int pointer = mPointer; + + while(counter < mBuffer.length) + { + sb.append(mBuffer[pointer].getBit1() ? "1" : "0"); + sb.append(mBuffer[pointer++].getBit2() ? "1" : "0"); + + if(pointer >= mBuffer.length) + { + pointer = 0; + } + + counter++; + } + + if(this instanceof NIDDelayBuffer) + { + mLog.debug("NIDBUF: " + sb.toString() + " Length:" + mBuffer.length); + } + else + { + mLog.debug("SYNBUF: " + sb.toString() + " Length:" + mBuffer.length); + } + } + + /** + * Returns an ordered buffer of the internal circular buffer contents. + */ + public Dibit[] getBuffer() + { + Dibit[] transferBuffer = new Dibit[mBuffer.length]; + + int transferBufferPointer = 0; + int bufferPointer = mPointer; + + while(transferBufferPointer < transferBuffer.length) + { + transferBuffer[transferBufferPointer++] = mBuffer[bufferPointer++]; + + if(bufferPointer >= mBuffer.length) + { + bufferPointer = 0; + } + } + + return transferBuffer; + } + + public int[] getBufferAsArray() + { + Dibit[] dibits = getBuffer(); + + int[] bits = new int[dibits.length * 2]; + + for(int x = 0; x < dibits.length; x++) + { + if(dibits[x].getBit1()) + { + bits[x * 2] = 1; + } + if(dibits[x].getBit2()) + { + bits[x * 2 + 1] = 1; + } + } + + return bits; + } + + /** + * Places the dibit into the internal circular buffer, overwriting the oldest dibit. + */ + public void put(Dibit dibit) + { + mBuffer[mPointer++] = dibit; + + if(mPointer >= mBuffer.length) + { + mPointer = 0; + } + } + + /** + * Places the dibit into the internal circular buffer, overwriting and returning the + * oldest dibit. + */ + public Dibit getAndPut(Dibit dibit) + { + Dibit toReturn = mBuffer[mPointer]; + put(dibit); + return toReturn; + } + } + + public static int[] reverse(int[] values) + { + int[] reversed = new int[values.length]; + + for(int x = 0; x < values.length; x++) + { + reversed[values.length - x - 1] = values[x]; + } + + return reversed; + } + + public static void logNID(int[] nid, boolean corrected) + { + StringBuilder sb = new StringBuilder(); + for(int value: nid) + { + sb.append(value); + } + + if(corrected) + { + mLog.debug("C* NID: " + sb.toString()); + } + else + { + mLog.debug(" NID: " + sb.toString()); + } + } + + public static int getBitErrorCount(int[] a, int[] b) + { + Validate.isTrue(a.length == b.length, "Array lengths must be the same"); + + int count = 0; + + for(int x = 0; x < a.length; x++) + { + if(a[x] != b[x]) + { + count++; + } + } + + return count; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java new file mode 100644 index 00000000..8189e25d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.protocol.Protocol; + +public class P25DecodeEvent extends DecodeEvent +{ + /** + * Constucts a P25 decode event + * @param start + */ + public P25DecodeEvent(long start) + { + super(start); + setProtocol(Protocol.APCO25); + } + + /** + * Creates a new decode event builder with the specified start timestamp. + * @param timeStart for the event + * @return builder + */ + public static DecodeEventBuilder builder(long timeStart) + { + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + decodeEventBuilder.protocol(Protocol.APCO25); + return decodeEventBuilder; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java index 786bf482..35dc9068 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java @@ -15,7 +15,6 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.p25; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.dsp.symbol.Dibit; import io.github.dsheirer.dsp.symbol.DibitToByteBufferAssembler; import io.github.dsheirer.module.decode.Decoder; @@ -37,15 +36,13 @@ public abstract class P25Decoder extends Decoder implements ISourceEventListener private Broadcaster mDibitBroadcaster = new Broadcaster<>(); private DibitToByteBufferAssembler mByteBufferAssembler = new DibitToByteBufferAssembler(300); private P25MessageProcessor mMessageProcessor; - private AliasList mAliasList; private Listener mSourceEventListener; private double mSymbolRate; - public P25Decoder(double symbolRate, AliasList aliasList) + public P25Decoder(double symbolRate) { mSymbolRate = symbolRate; - mAliasList = aliasList; - mMessageProcessor = new P25MessageProcessor(mAliasList); + mMessageProcessor = new P25MessageProcessor(); mMessageProcessor.setMessageListener(getMessageListener()); mDibitBroadcaster.addListener(mByteBufferAssembler); } @@ -90,11 +87,6 @@ public abstract class P25Decoder extends Decoder implements ISourceEventListener return mSymbolRate; } - protected AliasList getAliasList() - { - return mAliasList; - } - /** * Current sample rate for this decoder */ diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java index b9a9cb4d..79e3be91 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.p25; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.dsp.filter.FilterFactory; import io.github.dsheirer.dsp.filter.design.FilterDesignException; import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; @@ -25,6 +28,7 @@ import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulator; import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer; import io.github.dsheirer.dsp.psk.pll.AdaptivePLLGainMonitor; import io.github.dsheirer.dsp.psk.pll.CostasLoop; +import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; import io.github.dsheirer.source.SourceEvent; import org.slf4j.Logger; @@ -42,7 +46,7 @@ public class P25DecoderC4FM extends P25Decoder protected DQPSKDecisionDirectedDemodulator mQPSKDemodulator; protected CostasLoop mCostasLoop; protected AdaptivePLLGainMonitor mPLLGainMonitor; - protected P25MessageFramer mMessageFramer; + protected P25MessageFramer2 mMessageFramer; private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl(32); private Map mBasebandFilters = new HashMap<>(); private ComplexFIRFilter2 mBasebandFilter; @@ -50,12 +54,10 @@ public class P25DecoderC4FM extends P25Decoder /** * P25 Phase 1 - standard C4FM modulation decoder. Uses Differential QPSK decoding with a Costas PLL and a * decision-directed phase and timing error detector. - * - * @param aliasList */ - public P25DecoderC4FM(AliasList aliasList) + public P25DecoderC4FM() { - super(4800.0, aliasList); + super(4800.0); setSampleRate(25000.0); } @@ -74,12 +76,12 @@ public class P25DecoderC4FM extends P25Decoder if(mMessageFramer != null) { getDibitBroadcaster().removeListener(mMessageFramer); - mMessageFramer.dispose(); } //The Costas Loop receives symbol-inversion correction requests when detected. //The PLL gain monitor receives sync detect/loss signals from the message framer - mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop, mPLLGainMonitor); + mMessageFramer = new P25MessageFramer2(mCostasLoop, DecoderType.P25_PHASE1.getProtocol().getBitRate()); + mMessageFramer.setSyncDetectListener(mPLLGainMonitor); mMessageFramer.setListener(getMessageProcessor()); mMessageFramer.setSampleRate(sampleRate); @@ -100,6 +102,8 @@ public class P25DecoderC4FM extends P25Decoder //User accounting of the incoming buffer is handled by the gain filter ReusableComplexBuffer gainApplied = mAGC.filter(basebandFiltered); + mMessageFramer.setCurrentTime(reusableComplexBuffer.getTimestamp()); + //User accounting of the filtered buffer is handled by the demodulator mQPSKDemodulator.receive(gainApplied); } @@ -164,7 +168,6 @@ public class P25DecoderC4FM extends P25Decoder mBasebandFilter.dispose(); mBasebandFilter = null; - mMessageFramer.dispose(); mMessageFramer = null; } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java index ea3ff3d8..6c92fc60 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java @@ -15,7 +15,6 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.p25; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulatorInstrumented; import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented; import io.github.dsheirer.dsp.psk.SymbolDecisionData; @@ -35,12 +34,9 @@ public class P25DecoderC4FMInstrumented extends P25DecoderC4FM /** * Instrumented version of the P25 C4FM decoder that supports registering listeners to provide access to data as * it is being processed by the decoder. - * - * @param aliasList */ - public P25DecoderC4FMInstrumented(AliasList aliasList) + public P25DecoderC4FMInstrumented() { - super(aliasList); } /** diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java index be99d126..101971d6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.p25; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.dsp.filter.FilterFactory; import io.github.dsheirer.dsp.filter.Window.WindowType; import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2; @@ -24,6 +27,7 @@ import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulator; import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer; import io.github.dsheirer.dsp.psk.pll.AdaptivePLLGainMonitor; import io.github.dsheirer.dsp.psk.pll.CostasLoop; +import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; import io.github.dsheirer.source.SourceEvent; @@ -32,13 +36,15 @@ import java.util.Map; public class P25DecoderLSM extends P25Decoder { +// private final static Logger mLog = LoggerFactory.getLogger(P25DecoderLSM.class); + protected static final float SAMPLE_COUNTER_GAIN = 0.3f; private Map mBasebandFilters = new HashMap<>(); private ComplexFIRFilter2 mBasebandFilter; private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl(32); protected DQPSKGardnerDemodulator mQPSKDemodulator; - protected P25MessageFramer mMessageFramer; + protected P25MessageFramer2 mMessageFramer; protected CostasLoop mCostasLoop; protected AdaptivePLLGainMonitor mPLLGainMonitor; protected InterpolatingSampleBuffer mInterpolatingSampleBuffer; @@ -46,12 +52,10 @@ public class P25DecoderLSM extends P25Decoder /** * P25 Phase 1 - linear simulcast modulation (LSM) decoder. Uses Differential QPSK decoding with a Costas PLL and * a gardner timing error detector. - * - * @param aliasList */ - public P25DecoderLSM(AliasList aliasList) + public P25DecoderLSM() { - super(4800.0, aliasList); + super(4800.0); setSampleRate(25000.0); } @@ -78,10 +82,10 @@ public class P25DecoderLSM extends P25Decoder if(mMessageFramer != null) { getDibitBroadcaster().removeListener(mMessageFramer); - mMessageFramer.dispose(); } - mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop, mPLLGainMonitor); + mMessageFramer = new P25MessageFramer2(mCostasLoop, DecoderType.P25_PHASE1.getProtocol().getBitRate()); + mMessageFramer.setSyncDetectListener(mPLLGainMonitor); mMessageFramer.setListener(getMessageProcessor()); mMessageFramer.setSampleRate(sampleRate); mQPSKDemodulator.setSymbolListener(getDibitBroadcaster()); @@ -100,6 +104,8 @@ public class P25DecoderLSM extends P25Decoder //AGC will decrement the user count when finished ReusableComplexBuffer gainApplied = mAGC.filter(basebandFiltered); + mMessageFramer.setCurrentTime(reusableComplexBuffer.getTimestamp()); + //Decoder will decrement the user count when finished mQPSKDemodulator.receive(gainApplied); } @@ -163,7 +169,6 @@ public class P25DecoderLSM extends P25Decoder mQPSKDemodulator.dispose(); mQPSKDemodulator = null; - mMessageFramer.dispose(); mMessageFramer = null; } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java index 8099d31e..ea56c150 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java @@ -15,7 +15,6 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.p25; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulatorInstrumented; import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented; import io.github.dsheirer.dsp.psk.SymbolDecisionData; @@ -37,12 +36,9 @@ public class P25DecoderLSMInstrumented extends P25DecoderLSM * a gardner timing error detector. * * Instrumented decoder instance. - * - * @param aliasList */ - public P25DecoderLSMInstrumented(AliasList aliasList) + public P25DecoderLSMInstrumented() { - super(aliasList); } /** diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java index 2fcdd2f3..8db5520f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java @@ -1,227 +1,212 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * 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.p25; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.ChangeChannelTimeoutEvent; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.channel.traffic.TrafficChannelAllocationEvent; +import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.Channel.ChannelType; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.controller.channel.ChannelEvent; +import io.github.dsheirer.controller.channel.IChannelEventListener; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.patch.PatchGroupManager; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; +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.ipv4.IPV4Packet; +import io.github.dsheirer.module.decode.ip.udp.UDPPacket; import io.github.dsheirer.module.decode.p25.message.P25Message; import io.github.dsheirer.module.decode.p25.message.hdu.HDUMessage; +import io.github.dsheirer.module.decode.p25.message.hdu.HeaderData; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCCallTermination; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCExtendedFunctionCommand; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCMessageUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCStatusUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCTelephoneInterconnectAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.ldu.EncryptionSyncParameters; import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.ldu.LDUMessage; -import io.github.dsheirer.module.decode.p25.message.ldu.lc.CallTermination; +import io.github.dsheirer.module.decode.p25.message.ldu.LDU2Message; import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.PDUTypeUnknown; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.PacketData; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.SNDCPUserData; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.CallAlertExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.StatusQueryExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.tdu.TDUMessage; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcastExplicit; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.CallAlert; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.ExtendedFunctionCommand; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.GroupAffiliationQuery; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdate; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdateExplicit; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.GroupVoiceChannelUser; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.MessageUpdate; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.NetworkStatusBroadcastExplicit; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.StatusQuery; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.StatusUpdate; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.TDULinkControlMessage; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.UnitAuthenticationCommand; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.UnitRegistrationCommand; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCAuthenticationResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCIndividualDataServiceRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCLocationRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCMessageUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCStatusQueryResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCStatusUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCUnitAcknowledgeResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCUnitToUnitVoiceServiceAnswerResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCUnitToUnitVoiceServiceRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupAffiliationResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCIndividualDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCMessageUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCProtectionParameterBroadcast; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCStatusUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCTelephoneInterconnectChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCTelephoneInterconnectChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitRegistrationResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitToUnitVoiceServiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitToUnitVoiceServiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.PacketMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacketMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing; +import io.github.dsheirer.module.decode.p25.message.tdu.TDULinkControlMessage; import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupAdd; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupDelete; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.DenyResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.IdentifierUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.QueuedResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import io.github.dsheirer.module.decode.p25.reference.IPProtocol; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Response; -import io.github.dsheirer.module.decode.p25.reference.Vendor; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.CancelServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.ExtendedFunctionResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.GroupAffiliationQueryResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.GroupDataServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.GroupVoiceServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.IndividualDataServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.LocationRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.MessageUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.SNDCPDataChannelRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.SNDCPDataPageResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.SNDCPReconnectRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.StatusQueryResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.StatusUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.TelephoneInterconnectAnswerResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitAcknowledgeResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitToUnitVoiceServiceAnswerResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitToUnitVoiceServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.AcknowledgeResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.DenyResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.ExtendedFunctionCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupAffiliationResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupVoiceChannelGrantUpdateExplicit; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.LocationRegistrationResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.MessageUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.QueuedResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.RoamingAddressCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SNDCPDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.StatusUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.TelephoneInterconnectAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.TelephoneInterconnectVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.TelephoneInterconnectVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitRegistrationResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitToUnitVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitToUnitVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.network.P25NetworkConfigurationMonitor; +import io.github.dsheirer.module.decode.p25.reference.Encryption; +import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -public class P25DecoderState extends DecoderState +/** + * Decoder state for an APCO25 channel. Maintains the call/data/idle state of the channel and produces events by + * monitoring the decoded message stream. + * + */ +public class P25DecoderState extends DecoderState implements IChannelEventListener { private final static Logger mLog = LoggerFactory.getLogger(P25DecoderState.class); - private static final DecimalFormat mFrequencyFormatter = - new DecimalFormat("0.000000"); - - - private NetworkStatusBroadcast mNetworkStatus; - private NetworkStatusBroadcastExtended mNetworkStatusExtended; - private ProtectionParameterBroadcast mProtectionParameterBroadcast; - private RFSSStatusBroadcast mRFSSStatusMessage; - private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; - private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; - - private Set mSecondaryControlChannels = - new TreeSet<>(); - - private Map mBands = new HashMap<>(); - private Map mRegistrations = new HashMap<>(); - private Map mNeighborMap = new HashMap<>(); - - private String mLastCommandEventID; - private String mLastPageEventID; - private String mLastQueryEventID; - private String mLastRegistrationEventID; - private String mLastResponseEventID; - - //TODO: create multi-attribute monitor for NAC and System - private String mNAC; - private String mSystem; - private AliasedStringAttributeMonitor mSiteAttributeMonitor; - private AliasedStringAttributeMonitor mFromTalkgroupMonitor; - private AliasedStringAttributeMonitor mToTalkgroupMonitor; - private String mCurrentChannel = "CURRENT"; - private long mCurrentChannelFrequency = 0; private ChannelType mChannelType; private P25Decoder.Modulation mModulation; - private boolean mIgnoreDataCalls; - private boolean mControlChannelShutdownLogged; + private PatchGroupManager mPatchGroupManager = new PatchGroupManager(); + private P25NetworkConfigurationMonitor mNetworkConfigurationMonitor; + private P25TrafficChannelManager mTrafficChannelManager; + private Listener mChannelEventListener; + private DecodeEvent mCurrentCallEvent; - private P25CallEvent mCurrentCallEvent; - private List mCallDetectTalkgroups = new ArrayList<>(); - private Map mChannelCallMap = new HashMap<>(); - private PatchGroupManager mPatchGroupManager; - - public P25DecoderState(AliasList aliasList, - ChannelType channelType, - P25Decoder.Modulation modulation, - boolean ignoreDataCalls) + /** + * Constructs an APCO-25 decoder state with an optional traffic channel manager. + * @param channel with configuration details + * @param trafficChannelManager for handling traffic channel grants. + */ + public P25DecoderState(Channel channel, P25TrafficChannelManager trafficChannelManager) { - super(aliasList); - mChannelType = channelType; - mModulation = modulation; - mIgnoreDataCalls = ignoreDataCalls; + mChannelType = channel.getChannelType(); + mModulation = ((DecodeConfigP25Phase1)channel.getDecodeConfiguration()).getModulation(); + mNetworkConfigurationMonitor = new P25NetworkConfigurationMonitor(mModulation); - mPatchGroupManager = new PatchGroupManager(aliasList, getCallEventBroadcaster()); - mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE); - mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mFromTalkgroupMonitor.addIllegalValue("000000"); - mToTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mToTalkgroupMonitor.addIllegalValue("0000"); - mToTalkgroupMonitor.addIllegalValue("000000"); + if(trafficChannelManager != null) + { + mTrafficChannelManager = trafficChannelManager; + mChannelEventListener = trafficChannelManager.getChannelEventListener(); + } + else + { + mChannelEventListener = channelEvent -> { + //do nothing with channel events if we're not configured to process traffic channels + }; + } } + /** + * Constructs an APCO-25 decoder state for a traffic channel. + * @param channel with configuration details + */ + public P25DecoderState(Channel channel) + { + this(channel, null); + } + + /** + * Modulation type for the decoder + */ public P25Decoder.Modulation getModulation() { return mModulation; } + /** + * Identifies the decoder type + */ @Override public DecoderType getDecoderType() { return DecoderType.P25_PHASE1; } + /** + * Implements the IChannelEventListener interface to receive traffic channel teardown notifications so that the + * traffic channel manager can manage traffic channel allocations. + */ + @Override + public Listener getChannelEventListener() + { + return mChannelEventListener; + } + /** * Performs a full reset to prepare this object for reuse on a new channel */ @@ -229,3134 +214,2171 @@ public class P25DecoderState extends DecoderState public void reset() { resetState(); - - mNAC = null; - mSiteAttributeMonitor.reset(); - mSystem = null; } - @Override - public void init() - { - - } - - /** +/** * Resets any temporal state details */ - private void resetState() + protected void resetState() { - mFromTalkgroupMonitor.reset(); - mToTalkgroupMonitor.reset(); - - mCallDetectTalkgroups.clear(); + super.resetState(); if(mCurrentCallEvent != null) { - mCurrentCallEvent.end(); + mCurrentCallEvent.end(System.currentTimeMillis()); broadcast(mCurrentCallEvent); - } - - mCurrentCallEvent = null; - } - - /** - * Indicates if there a call event exists for the specified channel and - * from and to talkgroup identifiers. - * - * @param channel - channel number - * @param from user id - * @param to talkgroup or user id - * @return true if there is a call event - */ - public boolean hasCallEvent(String channel, String from, String to) - { - boolean hasEvent = false; - - if(mChannelCallMap.containsKey(channel)) - { - P25CallEvent event = mChannelCallMap.get(channel); - - if(to != null && - event.getToID() != null && - to.contentEquals(event.getToID())) - { - if(from != null) - { - if(event.getFromID() == null) - { - hasEvent = true; - } - else if(from.contentEquals(event.getFromID())) - { - hasEvent = true; - } - } - else - { - hasEvent = true; - } - } - } - - return hasEvent; - } - - /** - * Adds the channel and event to the current channel call map. If an entry - * already exists, terminates the event and broadcasts the update. - * - * @param event to place in the map - */ - public void registerCallEvent(P25CallEvent event) - { - String channel = event.getChannel(); - - if(mChannelCallMap.containsKey(event.getChannel())) - { - P25CallEvent previousEvent = mChannelCallMap.remove(event.getChannel()); - - previousEvent.end(); - - broadcast(previousEvent); - } - - mChannelCallMap.put(event.getChannel(), event); - } - - private void updateCallEvent(String channel, String from, String to) - { - P25CallEvent event = mChannelCallMap.get(channel); - - if(event != null && - to != null && - event.getToID() != null && - event.getToID().contentEquals(to)) - { - if(event.getFromID() == null && - from != null && - !from.contentEquals("0000") && - !from.contentEquals("000000")) - { - event.setFromID(from); - broadcast(event); - } + mCurrentCallEvent = null; } } /** * Primary message processing method. */ - public void receive(Message message) + @Override + public void receive(IMessage iMessage) { - if(message instanceof P25Message) + if(iMessage instanceof P25Message) { - updateNAC(((P25Message)message).getNAC()); + P25Message message = (P25Message)iMessage; - /* Voice Vocoder messages */ - if(message instanceof LDUMessage) - { - processLDU((LDUMessage)message); - } + getIdentifierCollection().update(message.getNAC()); - /* Trunking Signalling Messages */ - else if(message instanceof TSBKMessage) + switch(message.getDUID()) { - processTSBK((TSBKMessage)message); - } - - /* Terminator Data Unit with Link Control Message */ - else if(message instanceof TDULinkControlMessage) - { - processTDULC((TDULinkControlMessage)message); - } - - /* Packet Data Unit Messages */ - else if(message instanceof PDUMessage) - { - processPDU((PDUMessage)message); - } - - /* Header Data Unit Message - preceeds voice LDUs */ - else if(message instanceof HDUMessage) - { - processHDU((HDUMessage)message); - } - - /* Terminator Data Unit, or default message if CRC failed */ - else if(message instanceof TDUMessage) - { - processTDU((TDUMessage)message); + case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: + processAMBTC(message); + break; + case HEADER_DATA_UNIT: + processHDU((HDUMessage)message); + break; + case IP_PACKET_DATA: + processPacketData(message); + break; + case LOGICAL_LINK_DATA_UNIT_1: + case LOGICAL_LINK_DATA_UNIT_2: + processLDU(message); + break; + case PACKET_DATA_UNIT: + processPDU(message); + break; + case SUBNETWORK_DEPENDENT_CONVERGENCE_PROTOCOL: + processSNDCP(message); + break; + case TERMINATOR_DATA_UNIT: + processTDU(message); + break; + case TERMINATOR_DATA_UNIT_LINK_CONTROL: + processTDULC(message); + break; + case TRUNKING_SIGNALING_BLOCK_1: + case TRUNKING_SIGNALING_BLOCK_2: + case TRUNKING_SIGNALING_BLOCK_3: + processTSBK(message); + break; + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: + processUMBTC(message); + break; + case UNKNOWN: + break; } } } /** - * Terminator Data Unit + * Alternate Multi-Block Trunking Control (AMBTC) + * + * @param message */ - private void processTDU(TDUMessage tdu) + private void processAMBTC(P25Message message) { - if(mChannelType == ChannelType.STANDARD) + if(message instanceof AMBTCMessage && message.isValid()) { - broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE)); - } - } + AMBTCMessage ambtc = (AMBTCMessage)message; - /** - * Header Data Unit - first message in a call sequence. - */ - private void processHDU(HDUMessage hdu) - { - if(hdu.isValid()) - { - broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); - - String to = hdu.getToID(); - - mToTalkgroupMonitor.process(to); - - if(mCurrentCallEvent == null) + switch(ambtc.getHeader().getOpcode()) { - mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details((hdu.isEncryptedAudio() ? "ENCRYPTED " : "")) - .frequency(mCurrentChannelFrequency) - .to(to) - .build(); + case ISP_AUTHENTICATION_RESPONSE: + if(message instanceof AMBTCAuthenticationResponse) + { + AMBTCAuthenticationResponse ar = (AMBTCAuthenticationResponse)ambtc; - broadcast(mCurrentCallEvent); - } + MutableIdentifierCollection icAuthenticationResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icAuthenticationResponse.remove(IdentifierClass.USER); + icAuthenticationResponse.update(ambtc.getIdentifiers()); - } - } + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("AUTHENTICATION:" + ar.getAuthenticationValue()) + .identifiers(icAuthenticationResponse) + .build()); + } + break; + case ISP_CALL_ALERT_REQUEST: + MutableIdentifierCollection icCallAlertRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icCallAlertRequest.remove(IdentifierClass.USER); + icCallAlertRequest.update(ambtc.getIdentifiers()); - /** - * Terminator Data Unit with Link Control - transmitted multiple times at - * beginning or end of call sequence and includes embedded link control messages - */ - private void processTDULC(TDULinkControlMessage tdulc) - { - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - mCurrentCallEvent = null; - } - - if(tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION) - { - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - - } - else - { - broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE)); - } - - switch(tdulc.getOpcode()) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if(tdulc instanceof AdjacentSiteStatusBroadcast) - { - IAdjacentSite ias = (IAdjacentSite)tdulc; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - if(tdulc instanceof AdjacentSiteStatusBroadcastExplicit) - { - IAdjacentSite ias = (IAdjacentSite)tdulc; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - break; - case CALL_ALERT: - if(tdulc instanceof CallAlert) - { - CallAlert ca = - (CallAlert)tdulc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getSourceAddress()) - .to(ca.getTargetAddress()) + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) .details("CALL ALERT") + .identifiers(icCallAlertRequest) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case CALL_TERMINATION_OR_CANCELLATION: - /* This opcode as handled at the beginning of the method */ - break; - case CHANNEL_IDENTIFIER_UPDATE: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case EXTENDED_FUNCTION_COMMAND: - if(tdulc instanceof ExtendedFunctionCommand) - { - ExtendedFunctionCommand efc = (ExtendedFunctionCommand)tdulc; + break; + case ISP_GROUP_AFFILIATION_REQUEST: + MutableIdentifierCollection icGroupAffiliationRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icGroupAffiliationRequest.remove(IdentifierClass.USER); + icGroupAffiliationRequest.update(ambtc.getIdentifiers()); - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(efc.getTargetAddress()) - .details("FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument()) + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("GROUP AFFILIATION") + .identifiers(icGroupAffiliationRequest) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case GROUP_AFFILIATION_QUERY: - if(tdulc instanceof GroupAffiliationQuery) - { - GroupAffiliationQuery gaq = - (GroupAffiliationQuery)tdulc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION QUERY") - .from(gaq.getSourceAddress()) - .to(gaq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE: - /* Used only on trunked systems on the outbound channel, to - * reflect user activity on other channels. We process this - * as a call detect */ - if(tdulc instanceof GroupVoiceChannelUpdate) - { - GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; - - String groupA = gvcu.getGroupAddressA(); - - if(!mCallDetectTalkgroups.contains(groupA)) + break; + case ISP_INDIVIDUAL_DATA_SERVICE_REQUEST: + if(ambtc instanceof AMBTCIndividualDataServiceRequest) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelA()) - .details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyA()) - .to(groupA) + AMBTCIndividualDataServiceRequest idsr = (AMBTCIndividualDataServiceRequest)ambtc; + + MutableIdentifierCollection icDataRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icDataRequest.remove(IdentifierClass.USER); + icDataRequest.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("INDIVIDUAL DATA SERVICE " + idsr.getDataServiceOptions()) + .identifiers(icDataRequest) .build()); - - mCallDetectTalkgroups.add(groupA); } - - String groupB = gvcu.getGroupAddressB(); - - if(!mCallDetectTalkgroups.contains(groupB)) + break; + case ISP_LOCATION_REGISTRATION_REQUEST: + if(ambtc instanceof AMBTCLocationRegistrationRequest) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelB()) - .details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyB()) - .to(groupB) + AMBTCLocationRegistrationRequest lrr = (AMBTCLocationRegistrationRequest)ambtc; + + MutableIdentifierCollection icLocationRegistrationRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icLocationRegistrationRequest.remove(IdentifierClass.USER); + icLocationRegistrationRequest.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("LOCATION REGISTRATION - UNIQUE ID:" + lrr.getSourceId()) + .identifiers(icLocationRegistrationRequest) .build()); - - mCallDetectTalkgroups.add(groupB); } - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - /* Reflects other call activity on the system: CALL DETECT */ - if(mChannelType == ChannelType.STANDARD && - tdulc instanceof GroupVoiceChannelUpdateExplicit) - { - GroupVoiceChannelUpdateExplicit gvcue = - (GroupVoiceChannelUpdateExplicit)tdulc; - - String group = gvcue.getGroupAddress(); - - if(!mCallDetectTalkgroups.contains(group)) + break; + case ISP_MESSAGE_UPDATE_REQUEST: + if(ambtc instanceof AMBTCMessageUpdateRequest) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcue.getTransmitChannel()) - .details((gvcue.isEncrypted() ? "ENCRYPTED" : "")) - .frequency(gvcue.getDownlinkFrequency()) - .to(group) + AMBTCMessageUpdateRequest mur = (AMBTCMessageUpdateRequest)ambtc; + + MutableIdentifierCollection icMessageUpdateRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icMessageUpdateRequest.remove(IdentifierClass.USER); + icMessageUpdateRequest.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.SDM.toString()) + .details("MESSAGE:" + mur.getShortDataMessage()) + .identifiers(icMessageUpdateRequest) .build()); - - mCallDetectTalkgroups.add(group); } - } - break; - case GROUP_VOICE_CHANNEL_USER: - //Used on a traffic channel to reflect that the channel is currently being used by the - //TO talkgroup, but that channel is currently quiet (ie no voice) */ - if(tdulc instanceof GroupVoiceChannelUser) - { - GroupVoiceChannelUser gvcuser = (GroupVoiceChannelUser)tdulc; + break; + case ISP_ROAMING_ADDRESS_REQUEST: + MutableIdentifierCollection icRoamingAddressRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icRoamingAddressRequest.remove(IdentifierClass.USER); + icRoamingAddressRequest.update(ambtc.getIdentifiers()); - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE)); - - //The from value should always be "000000" indicating that nobody is currently talking - mFromTalkgroupMonitor.reset(); - - String to = gvcuser.getGroupAddress(); - mToTalkgroupMonitor.process(to); - } - break; - case MESSAGE_UPDATE: - if(tdulc instanceof MessageUpdate) - { - MessageUpdate mu = (MessageUpdate)tdulc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM) - .aliasList(getAliasList()) - .from(mu.getSourceAddress()) - .to(mu.getTargetAddress()) - .details("MSG: " + mu.getShortDataMessage()) + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("ROAMING ADDRESS") + .identifiers(icRoamingAddressRequest) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case NETWORK_STATUS_BROADCAST: - if(tdulc instanceof io.github.dsheirer.module.decode.p25.message.tdu.lc.NetworkStatusBroadcast) - { - updateSystem(((io.github.dsheirer.module.decode.p25.message.tdu.lc.NetworkStatusBroadcast)tdulc).getSystem()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if(tdulc instanceof NetworkStatusBroadcastExplicit) - { - updateSystem(((NetworkStatusBroadcastExplicit)tdulc).getSystem()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case PROTECTION_PARAMETER_BROADCAST: - if(tdulc instanceof io.github.dsheirer.module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast) - { - io.github.dsheirer.module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast ppb = - (io.github.dsheirer.module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast)tdulc; + break; + case ISP_STATUS_QUERY_REQUEST: + MutableIdentifierCollection icStatusQuery = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusQuery.remove(IdentifierClass.USER); + icStatusQuery.update(ambtc.getIdentifiers()); - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(ppb.getTargetAddress()) - .details("ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case RFSS_STATUS_BROADCAST: - if(tdulc instanceof io.github.dsheirer.module.decode.p25.message.tdu.lc.RFSSStatusBroadcast) - { - io.github.dsheirer.module.decode.p25.message.tdu.lc.RFSSStatusBroadcast rfsssb = - (io.github.dsheirer.module.decode.p25.message.tdu.lc.RFSSStatusBroadcast)tdulc; - - updateSystem(rfsssb.getSystem()); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - mSiteAttributeMonitor.process(site); - - if(mCurrentChannel == null || - !mCurrentChannel.contentEquals(rfsssb.getChannel())) - { - mCurrentChannel = rfsssb.getChannel(); - mCurrentChannelFrequency = rfsssb.getDownlinkFrequency(); - } - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if(tdulc instanceof RFSSStatusBroadcastExplicit) - { - RFSSStatusBroadcastExplicit rfsssbe = - (RFSSStatusBroadcastExplicit)tdulc; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - mSiteAttributeMonitor.process(site); - - if(mCurrentChannel == null || - !mCurrentChannel.contentEquals(rfsssbe.getTransmitChannel())) - { - mCurrentChannel = rfsssbe.getTransmitChannel(); - mCurrentChannelFrequency = rfsssbe.getDownlinkFrequency(); - } - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if(tdulc instanceof io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast) - { - io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast sccb = - (io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if(tdulc instanceof SecondaryControlChannelBroadcastExplicit) - { - SecondaryControlChannelBroadcastExplicit sccb = (SecondaryControlChannelBroadcastExplicit)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case STATUS_QUERY: - if(tdulc instanceof StatusQuery) - { - StatusQuery sq = (StatusQuery)tdulc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) .details("STATUS QUERY") - .from(sq.getSourceAddress()) - .to(sq.getTargetAddress()) + .identifiers(icStatusQuery) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case STATUS_UPDATE: - if(tdulc instanceof StatusUpdate) - { - StatusUpdate su = (StatusUpdate)tdulc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.STATUS) - .aliasList(getAliasList()) - .details("STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus()) - .from(su.getSourceAddress()) - .to(su.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if(tdulc instanceof TelephoneInterconnectAnswerRequest) - { - TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)tdulc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(tiar.getTelephoneNumber()) - .to(tiar.getTargetAddress()) - .details("TELEPHONE CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - if(mChannelType == ChannelType.STANDARD && - tdulc instanceof TelephoneInterconnectVoiceChannelUser) - { - TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)tdulc; - - String to = tivcu.getAddress(); - - mToTalkgroupMonitor.process(to); - - if(mCurrentCallEvent == null) + break; + case ISP_STATUS_QUERY_RESPONSE: + if(ambtc instanceof AMBTCStatusQueryResponse) { - mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details((tivcu.isEncrypted() ? "ENCRYPTED" : "") + - (tivcu.isEmergency() ? " EMERGENCY" : "")) - .frequency(mCurrentChannelFrequency) - .to(to) - .build(); + MutableIdentifierCollection icStatusQueryResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusQueryResponse.remove(IdentifierClass.USER); + icStatusQueryResponse.update(ambtc.getIdentifiers()); - broadcast(mCurrentCallEvent); + AMBTCStatusQueryResponse sqr = (AMBTCStatusQueryResponse)ambtc; + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + sqr.getUnitStatus() + " USER:" + sqr.getUserStatus()) + .identifiers(icStatusQueryResponse) + .build()); } - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if(tdulc instanceof UnitAuthenticationCommand) - { - UnitAuthenticationCommand uac = (UnitAuthenticationCommand)tdulc; + break; + case ISP_STATUS_UPDATE_REQUEST: + if(ambtc instanceof AMBTCStatusUpdateRequest) + { + AMBTCStatusUpdateRequest sur = (AMBTCStatusUpdateRequest)ambtc; - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(uac.getCompleteTargetAddress()) - .details("AUTHENTICATE") + MutableIdentifierCollection icStatusUpdateRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusUpdateRequest.remove(IdentifierClass.USER); + icStatusUpdateRequest.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + sur.getUnitStatus() + " USER:" + sur.getUserStatus()) + .identifiers(icStatusUpdateRequest) + .build()); + } + break; + case ISP_UNIT_ACKNOWLEDGE_RESPONSE: + if(ambtc instanceof AMBTCUnitAcknowledgeResponse) + { + AMBTCUnitAcknowledgeResponse uar = (AMBTCUnitAcknowledgeResponse)ambtc; + + MutableIdentifierCollection icUnitAcknowledge = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitAcknowledge.remove(IdentifierClass.USER); + icUnitAcknowledge.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("ACKNOWLEDGE:" + uar.getAcknowledgedService()) + .identifiers(icUnitAcknowledge) + .build()); + } + break; + case ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST: + if(ambtc instanceof AMBTCUnitToUnitVoiceServiceRequest) + { + AMBTCUnitToUnitVoiceServiceRequest uuvsr = (AMBTCUnitToUnitVoiceServiceRequest)ambtc; + + MutableIdentifierCollection icUnitVoiceService = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitVoiceService.remove(IdentifierClass.USER); + icUnitVoiceService.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("UNIT-2-UNIT VOICE SERVICE " + uuvsr.getVoiceServiceOptions()) + .identifiers(icUnitVoiceService) + .build()); + } + break; + case ISP_UNIT_TO_UNIT_ANSWER_RESPONSE: + if(ambtc instanceof AMBTCUnitToUnitVoiceServiceAnswerResponse) + { + AMBTCUnitToUnitVoiceServiceAnswerResponse uuvsar = (AMBTCUnitToUnitVoiceServiceAnswerResponse)ambtc; + + MutableIdentifierCollection icUnitAnswerResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitAnswerResponse.remove(IdentifierClass.USER); + icUnitAnswerResponse.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details(uuvsar.getAnswerResponse() + " UNIT-2-UNIT VOICE SERVICE " + + uuvsar.getVoiceServiceOptions()) + .identifiers(icUnitAnswerResponse) + .build()); + } + break; + + //Network configuration messages + case OSP_ADJACENT_STATUS_BROADCAST: + mNetworkConfigurationMonitor.process(ambtc); + break; + + //Channel grants + case OSP_GROUP_DATA_CHANNEL_GRANT: + if(ambtc instanceof AMBTCGroupDataChannelGrant) + { + AMBTCGroupDataChannelGrant gdcg = (AMBTCGroupDataChannelGrant)ambtc; + + if(mTrafficChannelManager != null) + { + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(gdcg.getIdentifiers()); + + mTrafficChannelManager.processChannelGrant(gdcg.getChannel(), gdcg.getDataServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_GROUP_VOICE_CHANNEL_GRANT: + if(ambtc instanceof AMBTCGroupVoiceChannelGrant) + { + AMBTCGroupVoiceChannelGrant gvcg = (AMBTCGroupVoiceChannelGrant)ambtc; + + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(gvcg.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(gvcg.getChannel(), gvcg.getVoiceServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT: + if(ambtc instanceof AMBTCIndividualDataChannelGrant) + { + AMBTCIndividualDataChannelGrant idcg = (AMBTCIndividualDataChannelGrant)ambtc; + + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(idcg.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(idcg.getChannel(), idcg.getDataServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + if(ambtc instanceof AMBTCTelephoneInterconnectChannelGrant) + { + AMBTCTelephoneInterconnectChannelGrant ticg = (AMBTCTelephoneInterconnectChannelGrant)ambtc; + + if(mTrafficChannelManager != null) + { + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(ticg.getIdentifiers()); + + mTrafficChannelManager.processChannelGrant(ticg.getChannel(), ticg.getVoiceServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + if(ambtc instanceof AMBTCTelephoneInterconnectChannelGrantUpdate) + { + AMBTCTelephoneInterconnectChannelGrantUpdate ticgu = (AMBTCTelephoneInterconnectChannelGrantUpdate)ambtc; + + if(mTrafficChannelManager != null) + { + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(ticgu.getIdentifiers()); + + mTrafficChannelManager.processChannelGrant(ticgu.getChannel(), ticgu.getVoiceServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + if(ambtc instanceof AMBTCUnitToUnitVoiceServiceChannelGrant) + { + AMBTCUnitToUnitVoiceServiceChannelGrant uuvscg = (AMBTCUnitToUnitVoiceServiceChannelGrant)ambtc; + + if(mTrafficChannelManager != null) + { + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(uuvscg.getIdentifiers()); + + mTrafficChannelManager.processChannelGrant(uuvscg.getChannel(), uuvscg.getVoiceServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + if(ambtc instanceof AMBTCUnitToUnitVoiceServiceChannelGrantUpdate) + { + AMBTCUnitToUnitVoiceServiceChannelGrantUpdate uuvscgu = (AMBTCUnitToUnitVoiceServiceChannelGrantUpdate)ambtc; + + if(mTrafficChannelManager != null) + { + MutableIdentifierCollection identifierCollection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifierCollection.remove(IdentifierClass.USER); + identifierCollection.update(uuvscgu.getIdentifiers()); + + mTrafficChannelManager.processChannelGrant(uuvscgu.getChannel(), uuvscgu.getVoiceServiceOptions(), + identifierCollection, ambtc.getHeader().getOpcode(), + ambtc.getTimestamp()); + } + } + break; + case OSP_UNIT_TO_UNIT_ANSWER_REQUEST: + MutableIdentifierCollection icUUAnswerRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUUAnswerRequest.remove(IdentifierClass.USER); + icUUAnswerRequest.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("ANSWER REQUEST") + .identifiers(icUUAnswerRequest) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case UNIT_REGISTRATION_COMMAND: - if(tdulc instanceof UnitRegistrationCommand) - { - UnitRegistrationCommand urc = (UnitRegistrationCommand)tdulc; + case OSP_CALL_ALERT: + MutableIdentifierCollection icCallAlert = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icCallAlert.remove(IdentifierClass.USER); + icCallAlert.update(ambtc.getIdentifiers()); - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(urc.getCompleteTargetAddress()) - .details("REGISTER") + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("CALL ALERT") + .identifiers(icCallAlert) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if(tdulc instanceof UnitToUnitAnswerRequest) - { - UnitToUnitAnswerRequest uuar = (UnitToUnitAnswerRequest)tdulc; + break; + case OSP_GROUP_AFFILIATION_QUERY: + MutableIdentifierCollection icGroupAffiliationQuery = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icGroupAffiliationQuery.remove(IdentifierClass.USER); + icGroupAffiliationQuery.update(ambtc.getIdentifiers()); - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(uuar.getSourceAddress()) - .to(uuar.getTargetAddress()) - .details("UNIT TO UNIT CALL ALERT") + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("GROUP AFFILIATION") + .identifiers(icGroupAffiliationQuery) .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - /* Used on traffic channels to indicate the current call entities */ - if(tdulc instanceof UnitToUnitVoiceChannelUser) - { - UnitToUnitVoiceChannelUser uuvcu = (UnitToUnitVoiceChannelUser)tdulc; + break; + case OSP_GROUP_AFFILIATION_RESPONSE: + if(ambtc instanceof AMBTCGroupAffiliationResponse) + { + AMBTCGroupAffiliationResponse gar = (AMBTCGroupAffiliationResponse)ambtc; - mFromTalkgroupMonitor.reset(); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(ambtc.getIdentifiers()); - String to = uuvcu.getTargetAddress(); - mToTalkgroupMonitor.process(to); - } - break; - default: - break; + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("AFFILIATION GROUP:" + gar.getGroupId() + + " ANNOUNCEMENT GROUP:" + gar.getAnnouncementGroupId()) + .identifiers(ic) + .build()); + } + break; + case OSP_MESSAGE_UPDATE: + if(ambtc instanceof AMBTCMessageUpdate) + { + AMBTCMessageUpdate mu = (AMBTCMessageUpdate)ambtc; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.SDM.toString()) + .details("MESSAGE:" + mu.getShortDataMessage()) + .identifiers(ic) + .build()); + } + break; + case OSP_PROTECTION_PARAMETER_BROADCAST: + if(ambtc instanceof AMBTCProtectionParameterBroadcast) + { + AMBTCProtectionParameterBroadcast ppb = (AMBTCProtectionParameterBroadcast)ambtc; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("USE ENCRYPTION " + ppb.getEncryptionKey() + + " OUTBOUND MI:" + ppb.getOutboundMessageIndicator() + + " INBOUND MI:" + ppb.getInboundMessageIndicator()) + .identifiers(ic) + .build()); + } + break; + case OSP_ROAMING_ADDRESS_UPDATE: + MutableIdentifierCollection icRoamingAddressUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icRoamingAddressUpdate.remove(IdentifierClass.USER); + icRoamingAddressUpdate.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("ROAMING ADDRESS UPDATE") + .identifiers(icRoamingAddressUpdate) + .build()); + break; + case OSP_ROAMING_ADDRESS_COMMAND: + MutableIdentifierCollection icRoamingAddressCommand = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icRoamingAddressCommand.remove(IdentifierClass.USER); + icRoamingAddressCommand.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("ROAMING ADDRESS") + .identifiers(icRoamingAddressCommand) + .build()); + break; + case OSP_STATUS_QUERY: + MutableIdentifierCollection icOStatusQuery = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icOStatusQuery.remove(IdentifierClass.USER); + icOStatusQuery.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("STATUS") + .identifiers(icOStatusQuery) + .build()); + break; + case OSP_STATUS_UPDATE: + if(ambtc instanceof AMBTCStatusUpdate) + { + AMBTCStatusUpdate su = (AMBTCStatusUpdate)ambtc; + + MutableIdentifierCollection icStatusUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusUpdate.remove(IdentifierClass.USER); + icStatusUpdate.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + su.getUnitStatus() + " USER:" + su.getUserStatus()) + .identifiers(icStatusUpdate) + .build()); + } + break; + case OSP_UNIT_REGISTRATION_RESPONSE: + if(ambtc instanceof AMBTCUnitRegistrationResponse) + { + AMBTCUnitRegistrationResponse urr = (AMBTCUnitRegistrationResponse)ambtc; + + MutableIdentifierCollection icUnitRegistrationResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitRegistrationResponse.remove(IdentifierClass.USER); + icUnitRegistrationResponse.update(ambtc.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(ambtc.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REGISTER.toString()) + .details(urr.getResponse() + " UNIT REGISTRATION") + .identifiers(icUnitRegistrationResponse) + .build()); + } + break; + default: + mLog.debug("Unrecognized AMBTC Opcode: " + ambtc.getHeader().getOpcode().name()); + break; + } } + + broadcast(new DecoderStateEvent(this, Event.DECODE, State.CONTROL)); } /** - * Log optional vendor format messages that we don't yet support so that we - * can understand those messages and eventually add support. + * Processes a Header Data Unit message and starts a new call event. */ - private void logAlternateVendorMessage(P25Message message) + private void processHDU(HDUMessage message) { -// if( message.isValid() ) -// { -// mLog.info( "PLEASE NOTIFY DEVELOPER - UNRECOGNIZED P25 VENDOR FORMAT -" -// + " DUID:" + message.getDUID() -// + " MSG:" + message.getBinaryMessage() -// + " CLASS:" + message.getClass() ); -// } + if(message.isValid()) + { + HeaderData headerData = message.getHeaderData(); + + if(headerData.isValid()) + { + closeCurrentCallEvent(message.getTimestamp()); + + for(Identifier identifier : headerData.getIdentifiers()) + { + //Add to the identifier collection after filtering through the patch group manager + getIdentifierCollection().update(mPatchGroupManager.update(identifier)); + } + + updateCurrentCall(headerData.isEncryptedAudio() ? DecodeEventType.CALL_ENCRYPTED : + DecodeEventType.CALL, null, message.getTimestamp()); + + return; + } + } + + broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE)); } + /** - * Processes LDU voice frame messages. Sends continuation events to keep - * the channel state synchronized and processes the embedded link control - * messages to capture/broadcast peripheral events like paging. + * Processes an LDU voice message and forwards Link Control and/or Encryption Sync Parameters for + * additional processing. + * + * @param message that is an instance of an LDU1 or LDU2 message */ - private void processLDU(LDUMessage ldu) + private void processLDU(P25Message message) { broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); + if(message instanceof LDU1Message) + { + LinkControlWord lcw = ((LDU1Message)message).getLinkControlWord(); + + if(lcw != null && lcw.isValid()) + { + processLinkControl(lcw, message.getTimestamp()); + } + + updateCurrentCall(DecodeEventType.CALL, null, message.getTimestamp()); + } + else if(message instanceof LDU2Message) + { + EncryptionSyncParameters esp = ((LDU2Message)message).getEncryptionSyncParameters(); + + if(esp != null && esp.isValid()) + { + processEncryptionSyncParameters(esp, message.getTimestamp()); + } + + updateCurrentCall(DecodeEventType.CALL, null, message.getTimestamp()); + } + } + + /** + * Processes a Terminator Data Unit with Link Control (TDULC) message and forwards valid + * Link Control Word messages for additional processing. + * + * @param message that is an instance of a TDULC + */ + private void processTDULC(P25Message message) + { + closeCurrentCallEvent(message.getTimestamp()); + broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE)); + + if(message instanceof TDULinkControlMessage) + { + LinkControlWord lcw = ((TDULinkControlMessage)message).getLinkControlWord(); + + if(lcw != null && lcw.isValid()) + { + processLinkControl(lcw, message.getTimestamp()); + } + } + } + + /** + * Updates or creates a current call event. + * + * @param type of call that will be used as an event description + * @param details of the call (optional) + * @param timestamp of the message indicating a call or continuation + */ + private void updateCurrentCall(DecodeEventType type, String details, long timestamp) + { if(mCurrentCallEvent == null) { - mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .frequency(mCurrentChannelFrequency) + mCurrentCallEvent = P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(type.toString()) + .details(details) + .identifiers(getIdentifierCollection().copyOf()) .build(); broadcast(mCurrentCallEvent); - } - - if(ldu instanceof LDU1Message) - { - switch(((LDU1Message)ldu).getOpcode()) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast) - { - IAdjacentSite ias = (IAdjacentSite)ldu; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - else - { - logAlternateVendorMessage(ldu); - } - - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcastExplicit) - { - IAdjacentSite ias = (IAdjacentSite)ldu; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case CALL_ALERT: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.CallAlert) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.CallAlert ca = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.CallAlert)ldu; - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getSourceAddress()) - .to(ca.getTargetAddress()) - .details("CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case CALL_TERMINATION_OR_CANCELLATION: - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - mCurrentCallEvent = null; - - if(!(ldu instanceof CallTermination)) - { - logAlternateVendorMessage(ldu); - } - break; - case CHANNEL_IDENTIFIER_UPDATE: - //TODO: do we need this for the activity summary? - - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - //TODO: do we need this for the activity summary? - - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case EXTENDED_FUNCTION_COMMAND: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.ExtendedFunctionCommand) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.ExtendedFunctionCommand efc = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.ExtendedFunctionCommand)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(efc.getTargetAddress()) - .details("FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_AFFILIATION_QUERY: - if(mChannelType == ChannelType.STANDARD && - ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupAffiliationQuery) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupAffiliationQuery gaq = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupAffiliationQuery)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION QUERY") - .from(gaq.getSourceAddress()) - .to(gaq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; - - String userA = gvcu.getGroupAddressA(); - String userB = gvcu.getGroupAddressB(); - - if(mChannelType == ChannelType.STANDARD) - { - if(!mCallDetectTalkgroups.contains(userA)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelA()) - .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyA()) - .to(userA) - .build()); - - mCallDetectTalkgroups.add(userA); - } - - if(userB != null && - !userB.contentEquals("0000") && - !mCallDetectTalkgroups.contains(userB)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelB()) - .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyB()) - .to(userB) - .build()); - - mCallDetectTalkgroups.add(userB); - } - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; - - String group = gvcue.getGroupAddress(); - - if(mChannelType == ChannelType.STANDARD) - { - if(!mCallDetectTalkgroups.contains(group)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcue.getTransmitChannel()) - .details((gvcue.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) - .frequency(gvcue.getDownlinkFrequency()) - .to(group) - .build()); - - mCallDetectTalkgroups.add(group); - } - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_VOICE_CHANNEL_USER: - /* Indicates the current user of the current channel */ - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUser) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; - - mFromTalkgroupMonitor.process(gvcuser.getSourceAddress()); - mCurrentCallEvent.setFromID(mFromTalkgroupMonitor.getValue()); - - mToTalkgroupMonitor.process(gvcuser.getGroupAddress()); - - String to = mToTalkgroupMonitor.getValue(); - - if(mCurrentCallEvent.getToID() == null || !mCurrentCallEvent.getToID().contentEquals(to)) - { - mCurrentCallEvent.setToID(to); - } - - if(mChannelType == ChannelType.STANDARD) - { - if(mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.GROUP_CALL) - { - mCurrentCallEvent.setCallEventType(CallEvent.CallEventType.GROUP_CALL); - broadcast(mCurrentCallEvent); - } - - if(mCurrentCallEvent.getDetails() == null) - { - mCurrentCallEvent.setDetails( - (gvcuser.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + - (gvcuser.isEmergency() ? "EMERGENCY " : "")); - - broadcast(mCurrentCallEvent); - } - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case MESSAGE_UPDATE: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.MessageUpdate) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.MessageUpdate mu = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.MessageUpdate)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM) - .aliasList(getAliasList()) - .from(mu.getSourceAddress()) - .to(mu.getTargetAddress()) - .details("MSG: " + mu.getShortDataMessage()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case NETWORK_STATUS_BROADCAST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.NetworkStatusBroadcast) - { - updateSystem(((io.github.dsheirer.module.decode.p25.message.ldu.lc.NetworkStatusBroadcast)ldu).getSystem()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit) - { - updateSystem(((io.github.dsheirer.module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit)ldu).getSystem()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case PROTECTION_PARAMETER_BROADCAST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast ppb = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(ppb.getTargetAddress()) - .details("ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case RFSS_STATUS_BROADCAST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.RFSSStatusBroadcast) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.RFSSStatusBroadcast rfsssb = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.RFSSStatusBroadcast)ldu; - - updateSystem(rfsssb.getSystem()); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit)ldu; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast sccb = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit sccb = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case STATUS_QUERY: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.StatusQuery) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.StatusQuery sq = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.StatusQuery)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceAddress()) - .to(sq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case STATUS_UPDATE: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.StatusUpdate) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.StatusUpdate su = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.StatusUpdate)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.STATUS) - .aliasList(getAliasList()) - .details("STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus()) - .from(su.getSourceAddress()) - .to(su.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest tiar = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(tiar.getTelephoneNumber()) - .to(tiar.getTargetAddress()) - .details("TELEPHONE CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - /* Indicates the current user of the current channel */ - if(mChannelType == ChannelType.STANDARD && - ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser tivcu = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser)ldu; - - mToTalkgroupMonitor.process(tivcu.getAddress()); - - if(mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.TELEPHONE_INTERCONNECT) - { - mCurrentCallEvent.setCallEventType(CallEvent.CallEventType.TELEPHONE_INTERCONNECT); - broadcast(mCurrentCallEvent); - } - - if(mCurrentCallEvent.getDetails() == null) - { - mCurrentCallEvent.setDetails( - (tivcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + - (tivcu.isEmergency() ? "EMERGENCY " : "")); - - broadcast(mCurrentCallEvent); - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitAuthenticationCommand) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitAuthenticationCommand uac = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitAuthenticationCommand)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(uac.getCompleteTargetAddress()) - .details("AUTHENTICATE") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_REGISTRATION_COMMAND: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitRegistrationCommand) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitRegistrationCommand urc = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitRegistrationCommand)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(urc.getCompleteTargetAddress()) - .details("REGISTER") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if(ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest uuar = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest)ldu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(uuar.getSourceAddress()) - .to(uuar.getTargetAddress()) - .details("UNIT TO UNIT CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - if(mChannelType == ChannelType.STANDARD && - ldu instanceof io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser) - { - io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser uuvcu = - (io.github.dsheirer.module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser)ldu; - - mFromTalkgroupMonitor.process(uuvcu.getSourceAddress()); - - mToTalkgroupMonitor.process(uuvcu.getTargetAddress()); - - if(mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.UNIT_TO_UNIT_CALL) - { - mCurrentCallEvent.setCallEventType(CallEvent.CallEventType.UNIT_TO_UNIT_CALL); - broadcast(mCurrentCallEvent); - } - - if(mCurrentCallEvent.getDetails() == null) - { - mCurrentCallEvent.setDetails( - (uuvcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + - (uuvcu.isEmergency() ? "EMERGENCY " : "")); - - broadcast(mCurrentCallEvent); - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - default: - break; - } - } - } - - /** - * Process a Trunking Signalling Block message - */ - private void processTSBK(TSBKMessage tsbk) - { - /* Trunking Signalling Block Messages - indicates Control Channel */ - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - - if(tsbk.getVendor() == Vendor.STANDARD) - { - switch(tsbk.getOpcode()) - { - case ADJACENT_STATUS_BROADCAST: - if(tsbk instanceof AdjacentStatusBroadcast) - { - IAdjacentSite ias = (IAdjacentSite)tsbk; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - break; - case ACKNOWLEDGE_RESPONSE: - processTSBKResponse(tsbk); - break; - case AUTHENTICATION_COMMAND: - processTSBKCommand(tsbk); - break; - case CALL_ALERT: - processTSBKPage(tsbk); - break; - case DENY_RESPONSE: - processTSBKResponse(tsbk); - break; - case EXTENDED_FUNCTION_COMMAND: - processTSBKCommand(tsbk); - break; - case GROUP_AFFILIATION_QUERY: - processTSBKQuery(tsbk); - break; - case GROUP_AFFILIATION_RESPONSE: - processTSBKResponse(tsbk); - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - processTSBKDataChannelAnnouncement(tsbk); - break; - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - processTSBKChannelGrant(tsbk); - break; - case IDENTIFIER_UPDATE_NON_VUHF: - case IDENTIFIER_UPDATE_VHF_UHF_BANDS: - case IDENTIFIER_UPDATE_TDMA: - IdentifierUpdate iu = (IdentifierUpdate)tsbk; - - if(!mBands.containsKey(iu.getIdentifier())) - { - mBands.put(iu.getIdentifier(), iu); - } - break; - case LOCATION_REGISTRATION_RESPONSE: - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - processTSBKResponse(tsbk); - break; - case MESSAGE_UPDATE: - processTSBKMessage(tsbk); - break; - case NETWORK_STATUS_BROADCAST: - mNetworkStatus = (NetworkStatusBroadcast)tsbk; - break; - case PROTECTION_PARAMETER_UPDATE: - processTSBKResponse(tsbk); - break; - case QUEUED_RESPONSE: - processTSBKResponse(tsbk); - break; - case RADIO_UNIT_MONITOR_COMMAND: - processTSBKCommand(tsbk); - break; - case RFSS_STATUS_BROADCAST: - processTSBKRFSSStatus((RFSSStatusBroadcast)tsbk); - break; - case ROAMING_ADDRESS_COMMAND: - processTSBKCommand(tsbk); - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - SecondaryControlChannelBroadcast sccb = - (SecondaryControlChannelBroadcast)tsbk; - - if(sccb.getDownlinkFrequency1() > 0) - { - mSecondaryControlChannels.add(sccb); - } - break; - case SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)tsbk; - break; - case SNDCP_DATA_CHANNEL_GRANT: - processTSBKChannelGrant(tsbk); - break; - case STATUS_QUERY: - processTSBKQuery(tsbk); - break; - case STATUS_UPDATE: - processTSBKResponse(tsbk); - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - case UNIT_TO_UNIT_ANSWER_REQUEST: - processTSBKPage(tsbk); - break; - case UNIT_REGISTRATION_COMMAND: - processTSBKCommand(tsbk); - break; - case UNIT_REGISTRATION_RESPONSE: - processTSBKResponse(tsbk); - break; - default: - break; - } - } - else if(tsbk.getVendor() == Vendor.MOTOROLA) - { - processMotorolaTSBK((MotorolaTSBKMessage)tsbk); - } - } - - /** - * Process a Packet Data Unit message - */ - private void processPDU(PDUMessage pdu) - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); - - if(pdu instanceof PacketData || pdu instanceof PDUTypeUnknown) - { - } - else if(pdu instanceof PDUConfirmedMessage) - { - PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; - - switch(pduc.getPDUType()) - { - case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPActivateTDSContextAccept satca = - (SNDCPActivateTDSContextAccept)pduc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("ACTIVATE SNDCP USE IP:" + satca.getIPAddress()) - .frequency(mCurrentChannelFrequency) - .to(satca.getLogicalLinkID()) - .build()); - break; - case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: - SNDCPActivateTDSContextReject satcr = - (SNDCPActivateTDSContextReject)pduc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("REJECT: SNDCP CONTEXT ACTIVATION " - + "REASON:" + satcr.getReason().getLabel()) - .frequency(mCurrentChannelFrequency) - .to(satcr.getLogicalLinkID()) - .build()); - break; - case SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPActivateTDSContextRequest satcreq = - (SNDCPActivateTDSContextRequest)pduc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("REQUEST SNDCP USE IP:" + satcreq.getIPAddress()) - .frequency(mCurrentChannelFrequency) - .from(satcreq.getLogicalLinkID()) - .build()); - break; - case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPDeactivateTDSContext sdtca = - (SNDCPDeactivateTDSContext)pduc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("ACCEPT DEACTIVATE SNDCP CONTEXT") - .frequency(mCurrentChannelFrequency) - .from(sdtca.getLogicalLinkID()) - .build()); - break; - case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPDeactivateTDSContext sdtcreq = - (SNDCPDeactivateTDSContext)pduc; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("REQUEST DEACTIVATE SNDCP CONTEXT") - .frequency(mCurrentChannelFrequency) - .from(sdtcreq.getLogicalLinkID()) - .build()); - break; - case SNDCP_RF_CONFIRMED_DATA: - if(pduc instanceof SNDCPUserData) - { - SNDCPUserData sud = (SNDCPUserData)pduc; - - StringBuilder sbFrom = new StringBuilder(); - StringBuilder sbTo = new StringBuilder(); - - sbFrom.append(sud.getSourceIPAddress()); - sbTo.append(sud.getDestinationIPAddress()); - - if(sud.getIPProtocol() == IPProtocol.UDP) - { - sbFrom.append(":"); - sbFrom.append(sud.getUDPSourcePort()); - sbTo.append(":"); - sbTo.append(sud.getUDPDestinationPort()); - } - - mFromTalkgroupMonitor.reset(); - mFromTalkgroupMonitor.process(sbFrom.toString()); - mToTalkgroupMonitor.reset(); - mToTalkgroupMonitor.process(pduc.getLogicalLinkID()); - - broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("DATA: " + sud.getPayload() + - " RADIO IP:" + sbTo.toString()) - .frequency(mCurrentChannelFrequency) - .from(sbFrom.toString()) - .to(pduc.getLogicalLinkID()) - .build()); - } - else - { - mLog.error("Error - expected SNDCPUserData instance but class was: " + pduc.getClass() + - " and PDU Type:" + pduc.getPDUType().name()); - } - break; - case SNDCP_RF_UNCONFIRMED_DATA: - break; - default: -// mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); - break; - } + broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); } else { - switch(pdu.getOpcode()) + if(type == DecodeEventType.CALL_ENCRYPTED) { - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - processPDUChannelGrant(pdu); - break; - case ADJACENT_STATUS_BROADCAST: - if(pdu instanceof AdjacentStatusBroadcastExtended) + mCurrentCallEvent.setEventDescription(type.toString()); + mCurrentCallEvent.setDetails(details); + } + mCurrentCallEvent.setIdentifierCollection(getIdentifierCollection().copyOf()); + mCurrentCallEvent.end(timestamp); + broadcast(mCurrentCallEvent); + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); + } + } + + /** + * Ends/closes the current call event. + * + * @param timestamp of the message that indicates the event has ended. + */ + private void closeCurrentCallEvent(long timestamp) + { + if(mCurrentCallEvent != null) + { + mCurrentCallEvent.end(timestamp); + broadcast(mCurrentCallEvent); + mCurrentCallEvent = null; + + //Only clear the from identifier at this point ... the channel may still be allocated to the TO talkgroup + getIdentifierCollection().remove(IdentifierClass.USER, Role.FROM); + } + } + + /** + * Terminator Data Unit (TDU). + */ + private void processTDU(P25Message message) + { + closeCurrentCallEvent(message.getTimestamp()); + broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE)); + } + + /** + * Packet Data Unit + * + * @param message + */ + private void processPDU(P25Message message) + { + if(message.isValid() && message instanceof PDUMessage) + { + PDUMessage pdu = (PDUMessage)message; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(pdu.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.DATA_PACKET.toString()) + .details(pdu.toString()) + .identifiers(ic) + .build()); + + } + + broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); + } + + /** + * Unconfirmed Multi-Block Trunking Control (UMBTC) + * + * @param message + */ + private void processUMBTC(P25Message message) + { + if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing) + { + UMBTCTelephoneInterconnectRequestExplicitDialing tired = (UMBTCTelephoneInterconnectRequestExplicitDialing)message; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tired.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tired.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("TELEPHONE INTERCONNECT:" + tired.getTelephoneNumber()) + .identifiers(ic) + .build()); + } + + broadcast(new DecoderStateEvent(this, Event.DECODE, State.CONTROL)); + } + + /** + * IP Packet Data + * + * @param message + */ + private void processPacketData(P25Message message) + { + broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); + + if(message instanceof SNDCPPacketMessage) + { + SNDCPPacketMessage sndcp = (SNDCPPacketMessage)message; + getIdentifierCollection().update(sndcp.getIdentifiers()); + } + else if(message instanceof PacketMessage) + { + PacketMessage packetMessage = (PacketMessage)message; + + IPacket packet = packetMessage.getPacket(); + + if(packet instanceof IPV4Packet) + { + IPV4Packet ipv4 = (IPV4Packet)packet; + + IPacket ipPayload = ipv4.getPayload(); + + if(ipPayload instanceof UDPPacket) + { + UDPPacket udpPacket = (UDPPacket)ipPayload; + + IPacket udpPayload = udpPacket.getPayload(); + + Identifier from = packetMessage.getHeader().isOutbound() ? + ipv4.getHeader().getFromAddress() : packetMessage.getHeader().getLLID(); + Identifier to = packetMessage.getHeader().isOutbound() ? + packetMessage.getHeader().getLLID() : ipv4.getHeader().getToAddress(); + + getIdentifierCollection().update(from); + getIdentifierCollection().update(to); + + if(udpPayload instanceof ARSPacket) { - IAdjacentSite ias = (IAdjacentSite)pdu; + ARSPacket arsPacket = (ARSPacket)udpPayload; - mNeighborMap.put(ias.getUniqueID(), ias); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(message.getIdentifiers()); - updateSystem(ias.getSystemID()); - } - break; - case CALL_ALERT: - if(pdu instanceof CallAlertExtended) - { - CallAlertExtended ca = (CallAlertExtended)pdu; + DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString()) + .identifiers(ic) + .details(arsPacket.toString() + " " + ipv4.toString()) + .build(); - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getWACN() + "-" + ca.getSystemID() + "-" + - ca.getSourceID()) - .to(ca.getTargetAddress()) - .build()); + broadcast(packetEvent); } else { - logAlternateVendorMessage(pdu); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(message.getIdentifiers()); + + DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.UDP_PACKET.toString()) + .identifiers(ic) + .details(ipv4.toString()) + .build(); + + broadcast(packetEvent); } + } + } + } + } + + /** + * Sub-Network Dependent Convergence Protocol (SNDCP) + * + * @param message + */ + private void processSNDCP(P25Message message) + { + broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); + + if(message.isValid() && message instanceof SNDCPPacketMessage) + { + SNDCPPacketMessage sndcpPacket = (SNDCPPacketMessage)message; + + SNDCPMessage sndcpMessage = sndcpPacket.getSNDCPMessage(); + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(sndcpPacket.getIdentifiers()); + + switch(sndcpPacket.getSNDCPPacketHeader().getPDUType()) + { + case OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("SNDCP ACTIVATE TDS CONTEXT ACCEPT") + .identifiers(ic) + .build()); break; - case GROUP_AFFILIATION_QUERY: - if(pdu instanceof GroupAffiliationQueryExtended) - { - GroupAffiliationQueryExtended gaqe = - (GroupAffiliationQueryExtended)pdu; - - if(mLastQueryEventID == null || !gaqe.getTargetAddress() - .contentEquals(mLastQueryEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION") - .from(gaqe.getWACN() + "-" + gaqe.getSystemID() + - "-" + gaqe.getSourceID()) - .to(gaqe.getTargetAddress()) - .build()); - - mLastQueryEventID = gaqe.getToID(); - } - } + case OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("SNDCP DEACTIVATE TDS CONTEXT ACCEPT") + .identifiers(ic) + .build()); break; - case GROUP_AFFILIATION_RESPONSE: - if(pdu instanceof GroupAffiliationResponseExtended) - { - GroupAffiliationResponseExtended gar = - (GroupAffiliationResponseExtended)pdu; - - if(mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("AFFILIATION:" + gar.getResponse().name() + - " FOR GROUP:" + gar.getGroupWACN() + "-" + - gar.getGroupSystemID() + "-" + - gar.getGroupID() + " ANNOUNCEMENT GROUP:" + - gar.getAnnouncementGroupID()) - .from(gar.getSourceWACN() + "-" + - gar.getSourceSystemID() + "-" + - gar.getSourceID()) - .to(gar.getTargetAddress()) - .build()); - - mLastResponseEventID = gar.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } + case OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("SNDCP DEACTIVATE TDS CONTEXT") + .identifiers(ic) + .build()); break; - case MESSAGE_UPDATE: - if(pdu instanceof MessageUpdateExtended) - { - MessageUpdateExtended mu = (MessageUpdateExtended)pdu; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM) - .aliasList(getAliasList()) - .details("MESSAGE: " + mu.getMessage()) - .from(mu.getSourceWACN() + "-" + mu.getSourceSystemID() + - "-" + mu.getSourceID()) - .to(mu.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(pdu); - } + case OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("SNDCP ACTIVATE TDS CONTEXT REJECT") + .identifiers(ic) + .build()); break; - case NETWORK_STATUS_BROADCAST: - if(pdu instanceof NetworkStatusBroadcastExtended) - { - mNetworkStatusExtended = (NetworkStatusBroadcastExtended)pdu; - } - else - { - logAlternateVendorMessage(pdu); - } + case INBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("SNDCP ACTIVATE TDS CONTEXT") + .identifiers(ic) + .build()); break; - case PROTECTION_PARAMETER_BROADCAST: - if(pdu instanceof ProtectionParameterBroadcast) - { - mProtectionParameterBroadcast = - (ProtectionParameterBroadcast)pdu; - } - else - { - logAlternateVendorMessage(pdu); - } + case INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("SNDCP DEACTIVATE TDS CONTEXT ACCEPT") + .identifiers(ic) + .build()); break; - case RFSS_STATUS_BROADCAST: - if(pdu instanceof RFSSStatusBroadcastExtended) - { - mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)pdu; - - updateNAC(mRFSSStatusMessageExtended.getNAC()); - updateSystem(mRFSSStatusMessageExtended.getSystemID()); - mSiteAttributeMonitor.process(mRFSSStatusMessageExtended.getRFSubsystemID() + - "-" + mRFSSStatusMessageExtended.getSiteID()); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case ROAMING_ADDRESS_UPDATE: - if(pdu instanceof RoamingAddressUpdateExtended) - { - RoamingAddressUpdateExtended raue = - (RoamingAddressUpdateExtended)pdu; - - StringBuilder sb = new StringBuilder(); - sb.append("ROAMING ADDRESS STACK A:"); - sb.append(raue.getWACNA() + "-" + raue.getSystemIDA()); - - if(raue.isFormat2()) - { - sb.append(" B:"); - sb.append(raue.getWACNB() + "-" + raue.getSystemIDB()); - sb.append(" C:"); - sb.append(raue.getWACNC() + "-" + raue.getSystemIDC()); - sb.append(" D:"); - sb.append(raue.getWACND() + "-" + raue.getSystemIDD()); - } - - if(raue.isFormat3()) - { - sb.append(" E:"); - sb.append(raue.getWACNE() + "-" + raue.getSystemIDE()); - sb.append(" F:"); - sb.append(raue.getWACNF() + "-" + raue.getSystemIDF()); - sb.append(" G:"); - sb.append(raue.getWACNG() + "-" + raue.getSystemIDG()); - } - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details(sb.toString()) - .from(raue.getSourceID()) - .to(raue.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case STATUS_QUERY: - if(pdu instanceof StatusQueryExtended) - { - StatusQueryExtended sq = (StatusQueryExtended)pdu; - - if(mLastQueryEventID == null || !sq.getTargetAddress() - .contentEquals(mLastQueryEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceWACN() + "-" + - sq.getSourceSystemID() + "-" + - sq.getSourceID()) - .to(sq.getTargetAddress()) - .build()); - - mLastQueryEventID = sq.getToID(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case STATUS_UPDATE: - if(pdu instanceof StatusUpdateExtended) - { - StatusUpdateExtended su = (StatusUpdateExtended)pdu; - - if(mLastResponseEventID == null || !mLastResponseEventID - .contentEquals(su.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus()) - .from(su.getSourceWACN() + "-" + - su.getSourceSystemID() + "-" + - su.getSourceID()) - .to(su.getTargetAddress()) - .build()); - - mLastResponseEventID = su.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case UNIT_REGISTRATION_RESPONSE: - if(pdu instanceof UnitRegistrationResponseExtended) - { - UnitRegistrationResponseExtended urr = - (UnitRegistrationResponseExtended)pdu; - - if(urr.getResponse() == Response.ACCEPT) - { - mRegistrations.put(urr.getAssignedSourceAddress(), - System.currentTimeMillis()); - } - - if(mLastRegistrationEventID == null || !mLastRegistrationEventID - .contentEquals(urr.getAssignedSourceAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .details("REGISTRATION:" + urr.getResponse().name() + - " FOR EXTERNAL SYSTEM ADDRESS: " + - urr.getWACN() + "-" + - urr.getSystemID() + "-" + - urr.getSourceAddress() + - " SOURCE ID: " + urr.getSourceID()) - .from(urr.getAssignedSourceAddress()) - .build()); - - mLastRegistrationEventID = urr.getAssignedSourceAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if(pdu instanceof UnitToUnitAnswerRequestExplicit) - { - UnitToUnitAnswerRequestExplicit utuare = - (UnitToUnitAnswerRequestExplicit)pdu; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(utuare.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .details((utuare.isEmergency() ? "EMERGENCY" : "")) - .from(utuare.getWACN() + "-" + - utuare.getSystemID() + "-" + - utuare.getSourceID()) - .to(utuare.getTargetAddress()) - .build()); - - mLastPageEventID = utuare.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - default: + case INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: + broadcast(P25DecodeEvent.builder(message.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("SNDCP DEACTIVATE TDS CONTEXT") + .identifiers(ic) + .build()); break; } } } - private void processPDUChannelGrant(PDUMessage pdu) + /** + * Trunking Signalling Block (TSBK) + * + * @param message + */ + private void processTSBK(P25Message message) { - String channel = null; - String from = null; - String to = null; + broadcast(new DecoderStateEvent(this, Event.DECODE, State.CONTROL)); - switch(pdu.getOpcode()) + if(message.isValid() && message instanceof TSBKMessage) { - case GROUP_DATA_CHANNEL_GRANT: - if(pdu instanceof GroupDataChannelGrantExtended) - { - GroupDataChannelGrantExtended gdcge = - (GroupDataChannelGrantExtended)pdu; + TSBKMessage tsbk = (TSBKMessage)message; - channel = gdcge.getTransmitChannel(); - from = gdcge.getSourceAddress(); - to = gdcge.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) + switch(tsbk.getOpcode()) + { + //Channel Grant messages + case OSP_GROUP_DATA_CHANNEL_GRANT: + if(tsbk instanceof GroupDataChannelGrant) { - updateCallEvent(channel, from, to); + GroupDataChannelGrant gdcg = (GroupDataChannelGrant)tsbk; + + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + for(Identifier identifier : gdcg.getIdentifiers()) + { + identifiers.update(mPatchGroupManager.update(identifier)); + } + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(gdcg.getChannel(), gdcg.getDataServiceOptions(), + identifiers, tsbk.getOpcode(), gdcg.getTimestamp()); + } } - else + break; + case OSP_GROUP_VOICE_CHANNEL_GRANT: + if(tsbk instanceof GroupVoiceChannelGrant) { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((gdcge.isEncrypted() ? "ENCRYPTED" : "") + - (gdcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); + GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)tsbk; - registerCallEvent(callEvent); - broadcast(callEvent); + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + for(Identifier identifier : gvcg.getIdentifiers()) + { + identifiers.update(mPatchGroupManager.update(identifier)); + } + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(gvcg.getChannel(), gvcg.getVoiceServiceOptions(), + identifiers, tsbk.getOpcode(), gvcg.getTimestamp()); + } } - } - else - { - logAlternateVendorMessage(pdu); - } - - if(!mIgnoreDataCalls) - { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - if(pdu instanceof GroupVoiceChannelGrantExplicit) - { - GroupVoiceChannelGrantExplicit gvcge = - (GroupVoiceChannelGrantExplicit)pdu; - - channel = gvcge.getTransmitChannel(); - from = gvcge.getSourceAddress(); - to = gvcge.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) + break; + case OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE: + if(tsbk instanceof GroupVoiceChannelGrantUpdate) { - updateCallEvent(channel, from, to); + GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)tsbk; + + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiersA = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiersA.remove(IdentifierClass.USER); + identifiersA.update(mPatchGroupManager.update(gvcgu.getGroupAddressA())); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(gvcgu.getChannelA(), null, identifiersA, + tsbk.getOpcode(), gvcgu.getTimestamp()); + } + + if(gvcgu.hasGroupB()) + { + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiersB = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiersB.remove(IdentifierClass.USER); + identifiersB.update(mPatchGroupManager.update(gvcgu.getGroupAddressB())); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(gvcgu.getChannelB(), null, identifiersB, + tsbk.getOpcode(), gvcgu.getTimestamp()); + } + } } - else + break; + case OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + if(tsbk instanceof GroupVoiceChannelGrantUpdateExplicit) { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((gvcge.isEncrypted() ? "ENCRYPTED" : "") + - (gvcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(gvcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); + GroupVoiceChannelGrantUpdateExplicit gvcgue = (GroupVoiceChannelGrantUpdateExplicit)tsbk; - registerCallEvent(callEvent); - broadcast(callEvent); + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + identifiers.update(mPatchGroupManager.update(gvcgue.getGroupAddress())); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(gvcgue.getChannel(), gvcgue.getVoiceServiceOptions(), + identifiers, tsbk.getOpcode(), gvcgue.getTimestamp()); + } } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - if(pdu instanceof IndividualDataChannelGrantExtended) - { - IndividualDataChannelGrantExtended idcge = - (IndividualDataChannelGrantExtended)pdu; - - channel = idcge.getTransmitChannel(); - from = idcge.getSourceWACN() + "-" + - idcge.getSourceSystemID() + "-" + - idcge.getSourceAddress(); - to = idcge.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) + break; + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + if(tsbk instanceof UnitToUnitVoiceChannelGrant) { - updateCallEvent(channel, from, to); + UnitToUnitVoiceChannelGrant uuvcg = (UnitToUnitVoiceChannelGrant)tsbk; + + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + identifiers.update(uuvcg.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(uuvcg.getChannel(), null, identifiers, + tsbk.getOpcode(), uuvcg.getTimestamp()); + } } - else + break; + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + if(tsbk instanceof UnitToUnitVoiceChannelGrantUpdate) { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((idcge.isEncrypted() ? "ENCRYPTED" : "") + - (idcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(idcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); + UnitToUnitVoiceChannelGrantUpdate uuvcgu = (UnitToUnitVoiceChannelGrantUpdate)tsbk; - registerCallEvent(callEvent); - broadcast(callEvent); + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + identifiers.update(uuvcgu.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(uuvcgu.getChannel(), null, identifiers, + tsbk.getOpcode(), uuvcgu.getTimestamp()); + } } - - if(!mIgnoreDataCalls) + break; + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + if(tsbk instanceof TelephoneInterconnectVoiceChannelGrant) { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + TelephoneInterconnectVoiceChannelGrant tivcg = (TelephoneInterconnectVoiceChannelGrant)tsbk; + + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + identifiers.update(tivcg.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(tivcg.getChannel(), tivcg.getVoiceServiceOptions(), + identifiers, tsbk.getOpcode(), tivcg.getTimestamp()); + } } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectChannelGrantExplicit ticge = - (TelephoneInterconnectChannelGrantExplicit)pdu; - - channel = ticge.getTransmitChannel(); - - //We don't know if the subscriber is calling or being called, so - //we use the same address in both from/to fields - from = ticge.getAddress(); - to = ticge.getAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEvent.CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(channel) - .details((ticge.isEncrypted() ? "ENCRYPTED" : "") + - (ticge.isEmergency() ? " EMERGENCY" : "") + - " CALL TIMER:" + ticge.getCallTimer()) - .frequency(ticge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - if(pdu instanceof UnitToUnitVoiceChannelGrantExtended) - { - UnitToUnitVoiceChannelGrantExtended uuvcge = - (UnitToUnitVoiceChannelGrantExtended)pdu; - - channel = uuvcge.getTransmitChannel(); - from = uuvcge.getSourceWACN() + "-" + - uuvcge.getSourceSystemID() + "-" + - uuvcge.getSourceID(); - to = uuvcge.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) + break; + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + if(tsbk instanceof TelephoneInterconnectVoiceChannelGrantUpdate) { - updateCallEvent(channel, from, to); + TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = (TelephoneInterconnectVoiceChannelGrantUpdate)tsbk; + + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + identifiers.update(tivcgu.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(tivcgu.getChannel(), tivcgu.getVoiceServiceOptions(), + identifiers, tsbk.getOpcode(), tivcgu.getTimestamp()); + } } - else + break; + case OSP_SNDCP_DATA_CHANNEL_GRANT: + if(tsbk instanceof SNDCPDataChannelGrant) { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEvent.CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((uuvcge.isEncrypted() ? "ENCRYPTED" : "") + - (uuvcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(uuvcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); + SNDCPDataChannelGrant dcg = (SNDCPDataChannelGrant)tsbk; - registerCallEvent(callEvent); - broadcast(callEvent); + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + identifiers.update(dcg.getIdentifiers()); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(dcg.getChannel(), dcg.getServiceOptions(), + identifiers, tsbk.getOpcode(), dcg.getTimestamp()); + } } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - if(pdu instanceof UnitToUnitVoiceChannelGrantUpdateExtended) - { - UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = - (UnitToUnitVoiceChannelGrantUpdateExtended)pdu; - - channel = uuvcgue.getTransmitChannel(); - from = uuvcgue.getSourceWACN() + "-" + - uuvcgue.getSourceSystemID() + "-" + - uuvcgue.getSourceID(); - to = uuvcgue.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) + break; + case MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT: + if(tsbk instanceof PatchGroupVoiceChannelGrant) { - updateCallEvent(channel, from, to); + PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk; + + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiers = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiers.remove(IdentifierClass.USER); + for(Identifier identifier : pgvcg.getIdentifiers()) + { + identifiers.update(mPatchGroupManager.update(identifier)); + } + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(pgvcg.getChannel(), pgvcg.getVoiceServiceOptions(), + identifiers, tsbk.getOpcode(), pgvcg.getTimestamp()); + } } - else + break; + case MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE: + if(tsbk instanceof PatchGroupVoiceChannelGrantUpdate) { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEvent.CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((uuvcgue.isEncrypted() ? "ENCRYPTED" : "") + - (uuvcgue.isEmergency() ? " EMERGENCY" : "")) - .frequency(uuvcgue.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); + PatchGroupVoiceChannelGrantUpdate pgvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk; - registerCallEvent(callEvent); - broadcast(callEvent); + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiersPG1 = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiersPG1.remove(IdentifierClass.USER); + identifiersPG1.update(mPatchGroupManager.update(pgvcgu.getPatchGroup1())); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(pgvcgu.getChannel1(), null, identifiersPG1, + tsbk.getOpcode(), pgvcgu.getTimestamp()); + } + + if(pgvcgu.hasPatchGroup2()) + { + //Make a copy of current identifiers and remove current user identifiers and replace from message + MutableIdentifierCollection identifiersPG2 = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + identifiersPG2.remove(IdentifierClass.USER); + identifiersPG2.update(mPatchGroupManager.update(pgvcgu.getPatchGroup2())); + + if(mTrafficChannelManager != null) + { + mTrafficChannelManager.processChannelGrant(pgvcgu.getChannel2(), null, + identifiersPG2, tsbk.getOpcode(), pgvcgu.getTimestamp()); + } + } } + break; - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + //Network Configuration Messages + case MOTOROLA_OSP_TRAFFIC_CHANNEL_ID: + case MOTOROLA_OSP_SYSTEM_LOADING: + case MOTOROLA_OSP_CONTROL_CHANNEL_ID: + case MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN: + case OSP_IDENTIFIER_UPDATE_TDMA: + case OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS: + case OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + case OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + case OSP_TIME_DATE_ANNOUNCEMENT: + case OSP_TDMA_SYNC_BROADCAST: + case OSP_SYSTEM_SERVICE_BROADCAST: + case OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST: + case OSP_RFSS_STATUS_BROADCAST: + case OSP_NETWORK_STATUS_BROADCAST: + case OSP_ADJACENT_STATUS_BROADCAST: + case OSP_IDENTIFIER_UPDATE: + case OSP_PROTECTION_PARAMETER_BROADCAST: + case OSP_PROTECTION_PARAMETER_UPDATE: + mNetworkConfigurationMonitor.process(tsbk); + break; - } - else - { - logAlternateVendorMessage(pdu); - } - break; - default: - break; + case OSP_UNIT_TO_UNIT_ANSWER_REQUEST: + MutableIdentifierCollection icUnitAnswerRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitAnswerRequest.remove(IdentifierClass.USER); + icUnitAnswerRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("UNIT-TO-UNIT ANSWER REQUEST") + .identifiers(icUnitAnswerRequest) + .build()); + break; + case OSP_TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + if(tsbk instanceof TelephoneInterconnectAnswerRequest) + { + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)tsbk; + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("TELEPHONE ANSWER REQUEST: " + tiar.getTelephoneNumber()) + .identifiers(ic) + .build()); + } + break; + case OSP_SNDCP_DATA_PAGE_REQUEST: + MutableIdentifierCollection icDataPage = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icDataPage.remove(IdentifierClass.USER); + icDataPage.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("SNDCP DATA PAGE REQUEST") + .identifiers(icDataPage) + .build()); + break; + case OSP_STATUS_UPDATE: + if(tsbk instanceof StatusUpdate) + { + StatusUpdate su = (StatusUpdate)tsbk; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + su.getUnitStatus() + " USER:" + su.getUserStatus()) + .identifiers(ic) + .build()); + } + break; + case OSP_STATUS_QUERY: + MutableIdentifierCollection icStatusQuery = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusQuery.remove(IdentifierClass.USER); + icStatusQuery.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("STATUS") + .identifiers(icStatusQuery) + .build()); + break; + case OSP_MESSAGE_UPDATE: + if(tsbk instanceof MessageUpdate) + { + MessageUpdate mu = (MessageUpdate)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.SDM.toString()) + .details("MSG:" + mu.getShortDataMessage()) + .identifiers(ic) + .build()); + } + break; + case OSP_RADIO_UNIT_MONITOR_COMMAND: + MutableIdentifierCollection icUnitMonitor = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitMonitor.remove(IdentifierClass.USER); + icUnitMonitor.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("RADIO UNIT MONITOR") + .identifiers(icUnitMonitor) + .build()); + break; + case OSP_CALL_ALERT: + MutableIdentifierCollection icCallAlert = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icCallAlert.remove(IdentifierClass.USER); + icCallAlert.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("CALL ALERT") + .identifiers(icCallAlert) + .build()); + break; + case OSP_ACKNOWLEDGE_RESPONSE: + if(tsbk instanceof AcknowledgeResponse) + { + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + AcknowledgeResponse ar = (AcknowledgeResponse)tsbk; + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("ACKNOWLEDGE " + ar.getAcknowledgedServiceType().getDescription()) + .identifiers(ic) + .build()); + } + break; + case OSP_QUEUED_RESPONSE: + if(tsbk instanceof QueuedResponse) + { + QueuedResponse qr = (QueuedResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("QUEUED: " + qr.getQueuedResponseServiceType().getDescription() + + " REASON: " + qr.getQueuedResponseReason() + + " INFO: " + qr.getAdditionalInfo()) + .identifiers(ic) + .build()); + } + break; + case OSP_EXTENDED_FUNCTION_COMMAND: + if(tsbk instanceof ExtendedFunctionCommand) + { + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + ExtendedFunctionCommand efc = (ExtendedFunctionCommand)tsbk; + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("EXTENDED FUNCTION: " + efc.getExtendedFunction() + + " ARGUMENTS:" + efc.getArguments()) + .identifiers(ic) + .build()); + } + break; + case OSP_DENY_RESPONSE: + if(tsbk instanceof DenyResponse) + { + DenyResponse dr = (DenyResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("DENY: " + dr.getDeniedServiceType().getDescription() + + " REASON: " + dr.getDenyReason() + " - INFO: " + dr.getAdditionalInfo()) + .identifiers(ic) + .build()); + } + break; + case OSP_GROUP_AFFILIATION_RESPONSE: + if(tsbk instanceof GroupAffiliationResponse) + { + GroupAffiliationResponse gar = (GroupAffiliationResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details(gar.getAffiliationResponse() + + " AFFILIATION GROUP: " + gar.getGroupAddress() + + (gar.isGlobalAffiliation() ? " (GLOBAL)" : " (LOCAL)") + + " ANNOUNCEMENT GROUP:" + gar.getAnnouncementGroupAddress()) + .identifiers(ic) + .build()); + } + break; + case OSP_GROUP_AFFILIATION_QUERY: + MutableIdentifierCollection icGroupAffiliationQuery = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icGroupAffiliationQuery.remove(IdentifierClass.USER); + icGroupAffiliationQuery.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("GROUP AFFILIATION") + .identifiers(icGroupAffiliationQuery) + .build()); + break; + case OSP_LOCATION_REGISTRATION_RESPONSE: + if(tsbk instanceof LocationRegistrationResponse) + { + LocationRegistrationResponse lrr = (LocationRegistrationResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REGISTER.toString()) + .details(lrr.getResponse() + " LOCATION REGISTRATION - GROUP:" + lrr.getGroupAddress()) + .identifiers(ic) + .build()); + } + break; + case OSP_UNIT_REGISTRATION_RESPONSE: + if(tsbk instanceof UnitRegistrationResponse) + { + UnitRegistrationResponse urr = (UnitRegistrationResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REGISTER.toString()) + .details(urr.getResponse() + " UNIT REGISTRATION - UNIT ID:" + urr.getTargetUniqueId()) + .identifiers(ic) + .build()); + } + break; + case OSP_UNIT_REGISTRATION_COMMAND: + MutableIdentifierCollection icUnitRegistration = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitRegistration.remove(IdentifierClass.USER); + icUnitRegistration.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("UNIT REGISTRATION") + .identifiers(icUnitRegistration) + .build()); + break; + case OSP_AUTHENTICATION_COMMAND: + MutableIdentifierCollection icAuthenticationCommand = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icAuthenticationCommand.remove(IdentifierClass.USER); + icAuthenticationCommand.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("AUTHENTICATE") + .identifiers(icAuthenticationCommand) + .build()); + break; + case OSP_UNIT_DEREGISTRATION_ACKNOWLEDGE: + MutableIdentifierCollection icUnitDeregistration = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitDeregistration.remove(IdentifierClass.USER); + icUnitDeregistration.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.DEREGISTER.toString()) + .details("ACKNOWLEDGE UNIT DE-REGISTRATION") + .identifiers(icUnitDeregistration) + .build()); + break; + case OSP_ROAMING_ADDRESS_COMMAND: + if(tsbk instanceof RoamingAddressCommand) + { + RoamingAddressCommand rac = (RoamingAddressCommand)tsbk; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details(rac.getStackOperation() + " ROAMING ADDRESS") + .identifiers(ic) + .build()); + } + break; + + //MOTOROLA PATCH GROUP OPCODES + case MOTOROLA_OSP_PATCH_GROUP_ADD: + mPatchGroupManager.addPatchGroups(tsbk.getIdentifiers()); + break; + case MOTOROLA_OSP_PATCH_GROUP_DELETE: + mPatchGroupManager.removePatchGroups(tsbk.getIdentifiers()); + break; + + //STANDARD - INBOUND OPCODES + case ISP_GROUP_VOICE_SERVICE_REQUEST: + if(tsbk instanceof GroupVoiceServiceRequest) + { + GroupVoiceServiceRequest gvsr = (GroupVoiceServiceRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("GROUP VOICE SERVICE " + gvsr.getVoiceServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST: + if(tsbk instanceof UnitToUnitVoiceServiceRequest) + { + UnitToUnitVoiceServiceRequest uuvsr = (UnitToUnitVoiceServiceRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("UNIT-2-UNIT VOICE SERVICE " + uuvsr.getVoiceServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_UNIT_TO_UNIT_ANSWER_RESPONSE: + if(tsbk instanceof UnitToUnitVoiceServiceAnswerResponse) + { + UnitToUnitVoiceServiceAnswerResponse uuvsar = (UnitToUnitVoiceServiceAnswerResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details(uuvsar.getAnswerResponse() + " UNIT-2-UNIT VOICE SERVICE " + uuvsar.getVoiceServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_TELEPHONE_INTERCONNECT_PSTN_REQUEST: + MutableIdentifierCollection icInterconnect = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icInterconnect.remove(IdentifierClass.USER); + icInterconnect.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("TELEPHONE INTERCONNECT") + .identifiers(icInterconnect) + .build()); + break; + case ISP_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE: + if(tsbk instanceof TelephoneInterconnectAnswerResponse) + { + TelephoneInterconnectAnswerResponse tiar = (TelephoneInterconnectAnswerResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details(tiar.getAnswerResponse() + " TELEPHONE INTERCONNECT " + tiar.getVoiceServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_INDIVIDUAL_DATA_SERVICE_REQUEST: + if(tsbk instanceof IndividualDataServiceRequest) + { + IndividualDataServiceRequest idsr = (IndividualDataServiceRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("INDIVIDUAL DATA SERVICE " + idsr.getVoiceServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_GROUP_DATA_SERVICE_REQUEST: + if(tsbk instanceof GroupDataServiceRequest) + { + GroupDataServiceRequest gdsr = (GroupDataServiceRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("GROUP DATA SERVICE " + gdsr.getVoiceServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_SNDCP_DATA_CHANNEL_REQUEST: + if(tsbk instanceof SNDCPDataChannelRequest) + { + SNDCPDataChannelRequest sdcr = (SNDCPDataChannelRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("SNDCP DATA CHANNEL " + sdcr.getDataServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_SNDCP_DATA_PAGE_RESPONSE: + if(tsbk instanceof SNDCPDataPageResponse) + { + SNDCPDataPageResponse sdpr = (SNDCPDataPageResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details(sdpr.getAnswerResponse() + " SNDCP DATA " + sdpr.getDataServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_SNDCP_RECONNECT_REQUEST: + if(tsbk instanceof SNDCPReconnectRequest) + { + SNDCPReconnectRequest srr = (SNDCPReconnectRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("SNDCP RECONNECT " + (srr.hasDataToSend() ? "- DATA TO SEND " : "") + + srr.getDataServiceOptions()) + .identifiers(ic) + .build()); + } + break; + case ISP_STATUS_UPDATE_REQUEST: + if(tsbk instanceof StatusUpdateRequest) + { + StatusUpdateRequest sur = (StatusUpdateRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + sur.getUnitStatus() + " USER:" + sur.getUserStatus()) + .identifiers(ic) + .build()); + } + break; + case ISP_STATUS_QUERY_RESPONSE: + if(tsbk instanceof StatusQueryResponse) + { + StatusQueryResponse sqr = (StatusQueryResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + sqr.getUnitStatus() + " USER:" + sqr.getUserStatus()) + .identifiers(ic) + .build()); + } + break; + case ISP_STATUS_QUERY_REQUEST: + MutableIdentifierCollection icStatusQueryRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusQueryRequest.remove(IdentifierClass.USER); + icStatusQueryRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("UNIT AND USER STATUS") + .identifiers(icStatusQueryRequest) + .build()); + break; + case ISP_MESSAGE_UPDATE_REQUEST: + if(tsbk instanceof MessageUpdateRequest) + { + MessageUpdateRequest mur = (MessageUpdateRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.SDM.toString()) + .details("MESSAGE:" + mur.getShortDataMessage()) + .identifiers(ic) + .build()); + } + break; + case ISP_RADIO_UNIT_MONITOR_REQUEST: + MutableIdentifierCollection icUnitMonitorRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitMonitorRequest.remove(IdentifierClass.USER); + icUnitMonitorRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("RADIO UNIT MONITOR") + .identifiers(icUnitMonitorRequest) + .build()); + break; + case ISP_CALL_ALERT_REQUEST: + MutableIdentifierCollection icCallAlertRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icCallAlertRequest.remove(IdentifierClass.USER); + icCallAlertRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("CALL ALERT") + .identifiers(icCallAlertRequest) + .build()); + break; + case ISP_UNIT_ACKNOWLEDGE_RESPONSE: + if(tsbk instanceof UnitAcknowledgeResponse) + { + UnitAcknowledgeResponse uar = (UnitAcknowledgeResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("UNIT ACKNOWLEDGE:" + uar.getAcknowledgedServiceType().getDescription()) + .identifiers(ic) + .build()); + } + break; + case ISP_CANCEL_SERVICE_REQUEST: + if(tsbk instanceof CancelServiceRequest) + { + CancelServiceRequest csr = (CancelServiceRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("CANCEL SERVICE:" + csr.getServiceType() + + " REASON:" + csr.getCancelReason() + (csr.hasAdditionalInformation() ? + " INFO:" + csr.getAdditionalInformation() : "")) + .identifiers(ic) + .build()); + } + break; + case ISP_EXTENDED_FUNCTION_RESPONSE: + if(tsbk instanceof ExtendedFunctionResponse) + { + ExtendedFunctionResponse efr = (ExtendedFunctionResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("EXTENDED FUNCTION:" + efr.getExtendedFunction() + + " ARGUMENTS:" + efr.getArguments()) + .identifiers(ic) + .build()); + } + break; + case ISP_EMERGENCY_ALARM_REQUEST: + MutableIdentifierCollection icAlarm = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icAlarm.remove(IdentifierClass.USER); + icAlarm.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("EMERGENCY ALARM") + .identifiers(icAlarm) + .build()); + break; + case ISP_GROUP_AFFILIATION_REQUEST: + MutableIdentifierCollection icGroupAffiliationRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icGroupAffiliationRequest.remove(IdentifierClass.USER); + icGroupAffiliationRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("GROUP AFFILIATION") + .identifiers(icGroupAffiliationRequest) + .build()); + break; + case ISP_GROUP_AFFILIATION_QUERY_RESPONSE: + if(tsbk instanceof GroupAffiliationQueryResponse) + { + GroupAffiliationQueryResponse gaqr = (GroupAffiliationQueryResponse)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("AFFILIATION - GROUP:" + gaqr.getGroupAddress() + + " ANNOUNCEMENT GROUP:" + gaqr.getAnnouncementGroupAddress()) + .identifiers(ic) + .build()); + } + break; + case ISP_UNIT_DE_REGISTRATION_REQUEST: + MutableIdentifierCollection icDeregistrationRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icDeregistrationRequest.remove(IdentifierClass.USER); + icDeregistrationRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.DEREGISTER.toString()) + .details("UNIT DE-REGISTRATION REQUEST") + .identifiers(icDeregistrationRequest) + .build()); + break; + case ISP_UNIT_REGISTRATION_REQUEST: + if(tsbk instanceof UnitRegistrationRequest) + { + UnitRegistrationRequest urr = (UnitRegistrationRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REGISTER.toString()) + .details((urr.isEmergency() ? "EMERGENCY " : "") + + "UNIT REGISTRATION REQUEST - CAPABILITY:" + urr.getCapability()) + .identifiers(ic) + .build()); + } + break; + case ISP_LOCATION_REGISTRATION_REQUEST: + if(tsbk instanceof LocationRegistrationRequest) + { + LocationRegistrationRequest lrr = (LocationRegistrationRequest)tsbk; + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REGISTER.toString()) + .details((lrr.isEmergency() ? "EMERGENCY " : "") + + "LOCATION REGISTRATION REQUEST - CAPABILITY:" + lrr.getCapability()) + .identifiers(ic) + .build()); + } + break; + case ISP_PROTECTION_PARAMETER_REQUEST: + MutableIdentifierCollection icProtection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icProtection.remove(IdentifierClass.USER); + icProtection.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("ENCRYPTION PARAMETERS") + .identifiers(icProtection) + .build()); + break; + case ISP_IDENTIFIER_UPDATE_REQUEST: + MutableIdentifierCollection icIdentifierUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icIdentifierUpdate.remove(IdentifierClass.USER); + icIdentifierUpdate.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("FREQUENCY BAND DETAILS") + .identifiers(icIdentifierUpdate) + .build()); + break; + case ISP_ROAMING_ADDRESS_REQUEST: + MutableIdentifierCollection icRoamingRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icRoamingRequest.remove(IdentifierClass.USER); + icRoamingRequest.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.REQUEST.toString()) + .details("ROAMING ADDRESS") + .identifiers(new IdentifierCollection(tsbk.getIdentifiers())) + .build()); + break; + case ISP_ROAMING_ADDRESS_RESPONSE: + MutableIdentifierCollection icRoaming = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icRoaming.remove(IdentifierClass.USER); + icRoaming.update(tsbk.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.RESPONSE.toString()) + .details("ROAMING ADDRESS") + .identifiers(icRoaming) + .build()); + break; + default: + mLog.debug("Unrecognized TSBK Opcode: " + tsbk.getOpcode().name()); + break; + } } } - private void processTSBKCommand(TSBKMessage message) + /** + * Processes encryption sync parameters carried by an LDU2 message + * + * @param esp that is non-null and valid + */ + private void processEncryptionSyncParameters(EncryptionSyncParameters esp, long timestamp) { - switch(message.getOpcode()) + if(esp.isEncryptedAudio()) { - case AUTHENTICATION_COMMAND: - AuthenticationCommand ac = (AuthenticationCommand)message; + for(Identifier identifier : esp.getIdentifiers()) + { + //Add to the identifier collection after filtering through the patch group manager + getIdentifierCollection().update(mPatchGroupManager.update(identifier)); + } + Encryption encryption = Encryption.fromValue(esp.getEncryptionKey().getValue().getAlgorithm()); + updateCurrentCall(DecodeEventType.CALL_ENCRYPTED, "ALGORITHM:" + encryption.toString(), timestamp); + } + else + { + getIdentifierCollection().remove(Form.ENCRYPTION_KEY); + updateCurrentCall(DecodeEventType.CALL, null, timestamp); + } + } - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(ac.getFullTargetID())) + /** + * Processes a Link Control Word (LCW) that is carried by either an LDU1 or a TDULC message. + * + * @param lcw that is non-null and valid + */ + private void processLinkControl(LinkControlWord lcw, long timestamp) + { + switch(lcw.getOpcode()) + { + //Calls in-progress on this channel + case GROUP_VOICE_CHANNEL_USER: + case MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER: + case MOTOROLA_TALK_COMPLETE: + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: + case UNIT_TO_UNIT_VOICE_CHANNEL_USER: + for(Identifier identifier : lcw.getIdentifiers()) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .details("AUTHENTICATE") - .to(ac.getWACN() + "-" + ac.getSystemID() + "-" + - ac.getTargetID()) - .build()); - - mLastCommandEventID = ac.getFullTargetID(); + //Add to the identifier collection after filtering through the patch group manager + getIdentifierCollection().update(mPatchGroupManager.update(identifier)); } break; + + //Call termination + case CALL_TERMINATION_OR_CANCELLATION: + //Note: we only broadcast an END state if this is a network-commanded channel teardown + if(lcw instanceof LCCallTermination && ((LCCallTermination)lcw).isNetworkCommandedTeardown()) + { + broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); + } + break; + + //Calls in-progress on another channel + case GROUP_VOICE_CHANNEL_UPDATE: + case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: + break; + + //Network configuration messages + case ADJACENT_SITE_STATUS_BROADCAST: + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + case NETWORK_STATUS_BROADCAST: + case NETWORK_STATUS_BROADCAST_EXPLICIT: + case PROTECTION_PARAMETER_BROADCAST: + case RFSS_STATUS_BROADCAST: + case RFSS_STATUS_BROADCAST_EXPLICIT: + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + case SYSTEM_SERVICE_BROADCAST: + mNetworkConfigurationMonitor.process(lcw); + break; + + //Patch Group management + case MOTOROLA_PATCH_GROUP_ADD: + mPatchGroupManager.addPatchGroups(lcw.getIdentifiers()); + break; + case MOTOROLA_PATCH_GROUP_DELETE: + mPatchGroupManager.removePatchGroups(lcw.getIdentifiers()); + break; + case MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_UPDATE: + mPatchGroupManager.addPatchGroups(lcw.getIdentifiers()); + break; + + //Other events + case CALL_ALERT: + MutableIdentifierCollection icCallAlert = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icCallAlert.remove(IdentifierClass.USER); + icCallAlert.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("Call Alert") + .identifiers(icCallAlert) + .build()); + break; case EXTENDED_FUNCTION_COMMAND: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand efc = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(efc.getTargetAddress())) + if(lcw instanceof LCExtendedFunctionCommand) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.FUNCTION) - .aliasList(getAliasList()) - .details("EXTENDED FUNCTION: " + - efc.getExtendedFunction().getLabel()) - .from(efc.getSourceAddress()) - .to(efc.getTargetAddress()) - .build()); + LCExtendedFunctionCommand efc = (LCExtendedFunctionCommand)lcw; - mLastCommandEventID = efc.getTargetAddress(); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("Extended Function: " + efc.getExtendedFunction() + + " Arguments:" + efc.getExtendedFunctionArguments()) + .identifiers(ic) + .build()); } break; - case RADIO_UNIT_MONITOR_COMMAND: - RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(rumc.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .details("RADIO UNIT MONITOR") - .from(rumc.getSourceAddress()) - .to(rumc.getTargetAddress()) - .build()); - - mLastCommandEventID = rumc.getTargetAddress(); - } - break; - case ROAMING_ADDRESS_COMMAND: - RoamingAddressCommand rac = (RoamingAddressCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(rac.getTargetID())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .details(rac.getStackOperation().name() + - " ROAMING ADDRESS " + rac.getWACN() + "-" + - rac.getSystemID()) - .to(rac.getTargetID()) - .build()); - - mLastCommandEventID = rac.getTargetID(); - } - break; - case UNIT_REGISTRATION_COMMAND: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand urc = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(urc.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) - .aliasList(getAliasList()) - .details("REGISTER") - .from(urc.getSourceAddress()) - .to(urc.getTargetAddress()) - .build()); - - mLastCommandEventID = urc.getTargetAddress(); - } - break; - default: - break; - } - } - - private void processTSBKMessage(TSBKMessage message) - { - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.MessageUpdate mu = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.MessageUpdate)message; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM) - .aliasList(getAliasList()) - .details("MESSAGE: " + mu.getMessage()) - .from(mu.getSourceAddress()) - .to(mu.getTargetAddress()) - .build()); - } - - private void processTSBKQuery(TSBKMessage message) - { - switch(message.getOpcode()) - { case GROUP_AFFILIATION_QUERY: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery gaq = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery)message; + MutableIdentifierCollection icGroupAffiliation = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icGroupAffiliation.remove(IdentifierClass.USER); + icGroupAffiliation.update(lcw.getIdentifiers()); - if(mLastQueryEventID == null || !mLastQueryEventID - .contentEquals(gaq.getTargetAddress())) + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("Group Affiliation") + .identifiers(icGroupAffiliation) + .build()); + break; + case MESSAGE_UPDATE: + if(lcw instanceof LCMessageUpdate) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION") - .from(gaq.getSourceAddress()) - .to(gaq.getTargetAddress()) + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(lcw.getIdentifiers()); + + LCMessageUpdate mu = (LCMessageUpdate)lcw; + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.SDM.toString()) + .details("MSG:" + mu.getShortDataMessage()) + .identifiers(ic) .build()); } break; case STATUS_QUERY: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.StatusQuery sq = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.StatusQuery)message; + MutableIdentifierCollection icStatusQuery = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icStatusQuery.remove(IdentifierClass.USER); + icStatusQuery.update(lcw.getIdentifiers()); - if(mLastQueryEventID == null || !mLastQueryEventID - .contentEquals(sq.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceAddress()) - .to(sq.getTargetAddress()) - .build()); - } - break; - default: - break; - } - } - - private void processTSBKResponse(TSBKMessage message) - { - switch(message.getOpcode()) - { - case ACKNOWLEDGE_RESPONSE: - AcknowledgeResponse ar = (AcknowledgeResponse)message; - - if(mLastResponseEventID == null || !ar.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - String to = ar.getTargetAddress(); - - if(ar.hasAdditionalInformation() && ar.hasExtendedAddress()) - { - to = ar.getWACN() + "-" + ar.getSystemID() + "-" + - ar.getTargetAddress(); - } - - String from = null; - - if(ar.hasAdditionalInformation() && !ar.hasExtendedAddress()) - { - from = ar.getFromID(); - } - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("ACKNOWLEDGE") - .from(from) - .to(to) - .build()); - - mLastResponseEventID = ar.getTargetAddress(); - } - break; - case DENY_RESPONSE: - DenyResponse dr = (DenyResponse)message; - - if(mLastResponseEventID == null || !dr.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("DENY REASON: " + dr.getReason().name() + - " REQUESTED: " + dr.getServiceType().name()) - .from(dr.getSourceAddress()) - .to(dr.getTargetAddress()) - .build()); - - mLastResponseEventID = dr.getTargetAddress(); - } - break; - case GROUP_AFFILIATION_RESPONSE: - GroupAffiliationResponse gar = (GroupAffiliationResponse)message; - - if(mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("AFFILIATION:" + gar.getResponse().name() + - " FOR " + gar.getAffiliationScope() + - " GROUP:" + gar.getGroupAddress() + - " ANNOUNCEMENT GROUP:" + - gar.getAnnouncementGroupAddress()) - .to(gar.getTargetAddress()) - .build()); - - mLastResponseEventID = gar.getTargetAddress(); - } - break; - case LOCATION_REGISTRATION_RESPONSE: - LocationRegistrationResponse lrr = - (LocationRegistrationResponse)message; - - if(lrr.getResponse() == Response.ACCEPT) - { - mRegistrations.put(lrr.getTargetAddress(), - System.currentTimeMillis()); - } - - if(mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals(lrr.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .details("REGISTRATION:" + lrr.getResponse().name() + - " SITE: " + lrr.getRFSSID() + "-" + - lrr.getSiteID()) - .from(lrr.getTargetAddress()) - .to(lrr.getGroupAddress()) - .build()); - - mLastRegistrationEventID = lrr.getTargetAddress(); - } - break; - case PROTECTION_PARAMETER_UPDATE: - ProtectionParameterUpdate ppu = (ProtectionParameterUpdate)message; - - if(mLastResponseEventID == null || !ppu.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("USE ENCRYPTION ALGORITHM:" + - ppu.getAlgorithm().name() + " KEY:" + - ppu.getKeyID()) - .to(ppu.getTargetAddress()) - .build()); - - mLastResponseEventID = ppu.getTargetAddress(); - } - break; - case QUEUED_RESPONSE: - QueuedResponse qr = (QueuedResponse)message; - - if(mLastResponseEventID == null || !qr.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("QUEUED REASON: " + qr.getReason().name() + - " REQUESTED: " + qr.getServiceType().name()) - .from(qr.getSourceAddress()) - .to(qr.getTargetAddress()) - .build()); - - mLastResponseEventID = qr.getTargetAddress(); - } + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.QUERY.toString()) + .details("Status") + .identifiers(icStatusQuery) + .build()); break; case STATUS_UPDATE: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.StatusUpdate su = - (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.StatusUpdate)message; - - if(mLastResponseEventID == null || !su.getTargetAddress() - .contentEquals(mLastResponseEventID)) + if(lcw instanceof LCStatusUpdate) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus()) - .from(su.getSourceAddress()) - .to(su.getTargetAddress()) + LCStatusUpdate su = (LCStatusUpdate)lcw; + + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.STATUS.toString()) + .details("UNIT:" + su.getUnitStatus() + " USER:" + su.getUserStatus()) + .identifiers(ic) .build()); - - mLastResponseEventID = su.getTargetAddress(); - } - - break; - case UNIT_REGISTRATION_RESPONSE: - UnitRegistrationResponse urr = (UnitRegistrationResponse)message; - - if(urr.getResponse() == Response.ACCEPT) - { - mRegistrations.put(urr.getSourceAddress(), - System.currentTimeMillis()); - } - - if(mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals(urr.getSourceAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .details("REGISTRATION:" + urr.getResponse().name() + - " SYSTEM: " + urr.getSystemID() + - " SOURCE ID: " + urr.getSourceID()) - .from(urr.getSourceAddress()) - .build()); - - mLastRegistrationEventID = urr.getSourceAddress(); - } - break; - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - UnitDeregistrationAcknowledge udr = - (UnitDeregistrationAcknowledge)message; - - if(mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals(udr.getSourceID())) - { - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.DEREGISTER) - .aliasList(getAliasList()) - .from(udr.getSourceID()) - .build()); - - List keysToRemove = new ArrayList(); - - /* Remove this radio from the registrations set */ - for(String key : mRegistrations.keySet()) - { - if(key.startsWith(udr.getSourceID())) - { - keysToRemove.add(key); - } - } - - for(String key : keysToRemove) - { - mRegistrations.remove(key); - } - - mLastRegistrationEventID = udr.getSourceID(); - } - break; - default: - break; - } - } - - private void processTSBKRFSSStatus(RFSSStatusBroadcast message) - { - mRFSSStatusMessage = message; - - updateNAC(message.getNAC()); - - updateSystem(message.getSystemID()); - - mSiteAttributeMonitor.process(message.getRFSubsystemID() + "-" + message.getSiteID()); - } - - private void processTSBKDataChannelAnnouncement(TSBKMessage message) - { - switch(message.getOpcode()) - { - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - GroupDataChannelAnnouncement gdca = (GroupDataChannelAnnouncement)message; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.ANNOUNCEMENT) - .aliasList(getAliasList()) - .channel(gdca.getChannel1()) - .details((gdca.isEncrypted() ? "ENCRYPTED" : "") + - (gdca.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdca.getDownlinkFrequency1()) - .to(gdca.getGroupAddress1()) - .build()); - - if(gdca.hasChannelNumber2()) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.ANNOUNCEMENT) - .aliasList(getAliasList()) - .channel(gdca.getChannel2()) - .details((gdca.isEncrypted() ? "ENCRYPTED" : "") + - (gdca.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdca.getDownlinkFrequency2()) - .to(gdca.getGroupAddress2()) - .build()); - } - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - GroupDataChannelAnnouncementExplicit gdcae = - (GroupDataChannelAnnouncementExplicit)message; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(gdcae.getTransmitChannel()) - .details((gdcae.isEncrypted() ? "ENCRYPTED" : "") + - (gdcae.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdcae.getDownlinkFrequency()) - .to(gdcae.getGroupAddress()) - .build()); - break; - default: - break; - } - } - - /** - * Process Motorola vendor-specific Trunking Signaling Block messages - */ - private void processMotorolaTSBK(MotorolaTSBKMessage tsbk) - { - String channel; - String from; - String to; - - switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode()) - { - case PATCH_GROUP_CHANNEL_GRANT: - //Cleanup patch groups - auto-expire any patch groups before we allocate a channel - mPatchGroupManager.cleanupPatchGroups(); - - PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk; - - channel = pgvcg.getChannel(); - from = pgvcg.getSourceAddress(); - to = pgvcg.getPatchGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(pgvcg.isTDMAChannel() ? "TDMA " : ""); - details.append(pgvcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(pgvcg.getPriority()); - details.append(pgvcg.isEncryptedChannel() ? " ENCRYPTED" : ""); - details.append(" ").append(pgvcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(pgvcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!pgvcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case PATCH_GROUP_CHANNEL_GRANT_UPDATE: - //Cleanup patch groups - auto-expire any patch groups before we allocate a channel - mPatchGroupManager.cleanupPatchGroups(); - - PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk; - - channel = gvcgu.getChannel1(); - to = gvcgu.getPatchGroupAddress1(); - - if(hasCallEvent(channel, null, to)) - { - updateCallEvent(channel, null, to); - } - else - { - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((gvcgu.isTDMAChannel1() ? "TDMA " : "") + - (gvcgu.isEncrypted() ? "ENCRYPTED " : "")) - .frequency(gvcgu.getDownlinkFrequency1()) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgu.isTDMAChannel1()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - - String channel2 = gvcgu.getChannel2(); - String to2 = gvcgu.getPatchGroupAddress2(); - - if(hasCallEvent(channel2, null, to2)) - { - updateCallEvent(channel2, null, to2); - } - else - { - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel2) - .details((gvcgu.isTDMAChannel2() ? "TDMA " : "") + - (gvcgu.isEncrypted() ? "ENCRYPTED " : "")) - .frequency(gvcgu.getDownlinkFrequency2()) - .to(to2) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgu.isTDMAChannel2()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel2))); - } - - break; - case PATCH_GROUP_ADD: - PatchGroupAdd pga = (PatchGroupAdd)tsbk; - mPatchGroupManager.updatePatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups()); - break; - case PATCH_GROUP_DELETE: - PatchGroupDelete pgd = (PatchGroupDelete)tsbk; - mPatchGroupManager.removePatchGroup(pgd.getPatchGroupAddress()); - break; - case CCH_PLANNED_SHUTDOWN: - if(!mControlChannelShutdownLogged) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.NOTIFICATION) - .details("PLANNED CONTROL CHANNEL SHUTDOWN") - .build()); - - mControlChannelShutdownLogged = true; - } - break; - } - } - - /** - * Process a traffic channel allocation message - */ - private void processTSBKChannelGrant(TSBKMessage message) - { - //Cleanup patch groups - auto-expire any patch groups before we allocate a channel - mPatchGroupManager.cleanupPatchGroups(); - - String channel = null; - String from = null; - String to = null; - - switch(message.getOpcode()) - { - case GROUP_DATA_CHANNEL_GRANT: - GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; - - channel = gdcg.getChannel(); - from = gdcg.getSourceAddress(); - to = gdcg.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gdcg.isTDMAChannel() ? "TDMA " : ""); - details.append(gdcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gdcg.getPriority()).append(" "); - details.append(gdcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gdcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(gdcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!mIgnoreDataCalls && !gdcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; - - channel = gvcg.getChannel(); - from = gvcg.getSourceAddress(); - to = gvcg.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcg.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcg.getPriority()).append(" "); - details.append(gvcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(gvcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)message; - - channel = gvcgu.getChannel1(); - from = null; - to = gvcgu.getGroupAddress1(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcgu.getPriority()).append(" "); - details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcgu.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(gvcgu.getChannel1()) - .details(details.toString()) - .frequency(gvcgu.getDownlinkFrequency1()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgu.isTDMAChannel1()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - - if(gvcgu.hasChannelNumber2()) - { - channel = gvcgu.getChannel2(); - to = gvcgu.getGroupAddress2(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcgu.getPriority()).append(" "); - details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcgu.getSessionMode().name()); - - P25CallEvent event2 = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(gvcgu.getChannel2()) - .details(details.toString()) - .frequency(gvcgu.getDownlinkFrequency2()) - .to(gvcgu.getGroupAddress2()) - .build(); - - registerCallEvent(event2); - broadcast(event2); - } - - if(!gvcgu.isTDMAChannel2()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - GroupVoiceChannelGrantUpdateExplicit gvcgue = - (GroupVoiceChannelGrantUpdateExplicit)message; - - channel = gvcgue.getTransmitChannelIdentifier() + "-" + - gvcgue.getTransmitChannelNumber(); - from = null; - to = gvcgue.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcgue.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcgue.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcgue.getPriority()).append(" "); - details.append(gvcgue.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcgue.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(gvcgue.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgue.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; - - channel = idcg.getChannel(); - from = idcg.getSourceAddress(); - to = idcg.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(idcg.isTDMAChannel() ? "TDMA " : ""); - details.append(idcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(idcg.getPriority()).append(" "); - details.append(idcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(idcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(idcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!mIgnoreDataCalls && !idcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case SNDCP_DATA_CHANNEL_GRANT: - SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; - - channel = sdcg.getTransmitChannel(); - from = null; - to = sdcg.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(sdcg.isTDMAChannel() ? "TDMA " : ""); - details.append(sdcg.isEmergency() ? "EMERGENCY " : ""); - details.append(sdcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(sdcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(sdcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!mIgnoreDataCalls && !sdcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectVoiceChannelGrant tivcg = (TelephoneInterconnectVoiceChannelGrant)message; - - channel = tivcg.getChannel(); - from = null; - /* Address is ambiguous and could mean either source or target, - * so we'll place the value in the to field */ - to = tivcg.getAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(tivcg.isTDMAChannel() ? "TDMA " : ""); - details.append(tivcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(tivcg.getPriority()).append(" "); - details.append(tivcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(tivcg.getSessionMode().name()).append(" "); - details.append("CALL TIMER:").append(tivcg.getCallTimer()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEvent.CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(tivcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!tivcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = (TelephoneInterconnectVoiceChannelGrantUpdate)message; - - channel = tivcgu.getChannelIdentifier() + "-" + tivcgu.getChannelNumber(); - from = null; - - /* Address is ambiguous and could mean either source or target, - * so we'll place the value in the to field */ - to = tivcgu.getAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(tivcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(tivcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(tivcgu.getPriority()).append(" "); - details.append(tivcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(tivcgu.getSessionMode().name()).append(" "); - details.append("CALL TIMER:").append(tivcgu.getCallTimer()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEvent.CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(tivcgu.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!tivcgu.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - UnitToUnitVoiceChannelGrant uuvcg = (UnitToUnitVoiceChannelGrant)message; - - channel = uuvcg.getChannelIdentifier() + "-" + uuvcg.getChannelNumber(); - from = uuvcg.getSourceAddress(); - to = uuvcg.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(uuvcg.isTDMAChannel() ? "TDMA " : ""); - details.append(uuvcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(uuvcg.getPriority()).append(" "); - details.append(uuvcg.isEncryptedChannel() ? " ENCRYPTED " : ""); - details.append(uuvcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEvent.CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(uuvcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!uuvcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - UnitToUnitVoiceChannelGrantUpdate uuvcgu = (UnitToUnitVoiceChannelGrantUpdate)message; - - channel = uuvcgu.getChannelIdentifier() + "-" + uuvcgu.getChannelNumber(); - from = uuvcgu.getSourceAddress(); - to = uuvcgu.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(uuvcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(uuvcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(uuvcgu.getPriority()).append(" "); - details.append(uuvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(uuvcgu.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEvent.CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(uuvcgu.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!uuvcgu.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - default: - break; - } - } - - private void updateSystem(String system) - { - if(mSystem == null || (system != null && !mSystem.contentEquals(system))) - { - mSystem = system; - broadcastSystemAndNACUpdate(); - } - } - - /** - * Broadcasts an update to the NAC - */ - private void updateNAC(String nac) - { - if(mNAC == null || (nac != null && !mNAC.contentEquals(nac))) - { - mNAC = nac; - broadcastSystemAndNACUpdate(); - } - } - - private void broadcastSystemAndNACUpdate() - { - String label = String.format("SYS:%s NAC:%s", mSystem, mNAC); - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_1, label)); - } - - /** - * Process a unit paging event message - */ - private void processTSBKPage(TSBKMessage message) - { - switch(message.getOpcode()) - { - case CALL_ALERT: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.CallAlert ca = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.CallAlert)message; - - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getSourceID()) - .to(ca.getTargetAddress()) - .build()); - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest utuar = (io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest)message; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(utuar.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .details((utuar.isEmergency() ? "EMERGENCY" : "")) - .from(utuar.getSourceAddress()) - .to(utuar.getTargetAddress()) - .build()); - - mLastPageEventID = utuar.getTargetAddress(); - } - break; - case SNDCP_DATA_PAGE_REQUEST: - SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)message; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(sdpr.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .details("SNDCP DATA DAC: " + - sdpr.getDataAccessControl() + - " NSAPI:" + sdpr.getNSAPI()) - .to(sdpr.getTargetAddress()) - .build()); - - mLastPageEventID = sdpr.getTargetAddress(); } break; case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest tiar = - (io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest)message; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(tiar.getTargetAddress())) + if(lcw instanceof LCTelephoneInterconnectAnswerRequest) { - broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) - .aliasList(getAliasList()) - .details(("TELEPHONE INTERCONNECT")) - .from(tiar.getTelephoneNumber()) - .to(tiar.getTargetAddress()) - .build()); + LCTelephoneInterconnectAnswerRequest tiar = (LCTelephoneInterconnectAnswerRequest)lcw; - mLastPageEventID = tiar.getTargetAddress(); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("Telephone Call:" + tiar.getTelephoneNumber()) + .identifiers(ic) + .build()); } break; + case UNIT_AUTHENTICATION_COMMAND: + MutableIdentifierCollection icAuthenticationCommand = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icAuthenticationCommand.remove(IdentifierClass.USER); + icAuthenticationCommand.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("Authenticate Unit") + .identifiers(icAuthenticationCommand) + .build()); + break; + case UNIT_REGISTRATION_COMMAND: + MutableIdentifierCollection icUnitRegistrationCommand = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitRegistrationCommand.remove(IdentifierClass.USER); + icUnitRegistrationCommand.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.COMMAND.toString()) + .details("Unit Registration") + .identifiers(icUnitRegistrationCommand) + .build()); + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + MutableIdentifierCollection icUnitAnswerRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + icUnitAnswerRequest.remove(IdentifierClass.USER); + icUnitAnswerRequest.update(lcw.getIdentifiers()); + + broadcast(P25DecodeEvent.builder(timestamp) + .channel(getCurrentChannel()) + .eventDescription(DecodeEventType.PAGE.toString()) + .details("Unit-to-Unit Answer Request") + .identifiers(icUnitAnswerRequest) + .build()); + break; default: + mLog.info("Unrecognized/Unprocessed Link Control Word Opcode: " + lcw.getOpcode().name()); break; } } @@ -3364,187 +2386,7 @@ public class P25DecoderState extends DecoderState @Override public String getActivitySummary() { - StringBuilder sb = new StringBuilder(); - - sb.append("Activity Summary - Decoder:P25 ").append(getModulation().getLabel()).append("\n"); - sb.append(DIVIDER1); - sb.append("SITE "); - - if(mNetworkStatus != null) - { - sb.append("NAC:" + mNetworkStatus.getNAC()); - sb.append("h WACN:" + mNetworkStatus.getWACN()); - sb.append("h SYS:" + mNetworkStatus.getSystemID()); - sb.append("h [").append(mNetworkStatus.getNetworkCallsign()).append("]"); - sb.append(" LRA:" + mNetworkStatus.getLocationRegistrationArea()); - } - else if(mNetworkStatusExtended != null) - { - sb.append("NAC:" + mNetworkStatusExtended.getNAC()); - sb.append("h WACN:" + mNetworkStatusExtended.getWACN()); - sb.append("h SYS:" + mNetworkStatusExtended.getSystemID()); - sb.append("h [").append(mNetworkStatusExtended.getNetworkCallsign()).append("]"); - sb.append(" LRA:" + mNetworkStatusExtended.getLocationRegistrationArea()); - } - - String site = null; - - if(mRFSSStatusMessage != null) - { - site = mRFSSStatusMessage.getRFSubsystemID() + "-" + mRFSSStatusMessage.getSiteID(); - } - else if(mRFSSStatusMessageExtended != null) - { - site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + mRFSSStatusMessageExtended.getSiteID(); - } - - sb.append("h RFSS-SITE:").append(site).append("h"); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(site); - - if(siteAlias != null) - { - sb.append(" " + siteAlias.getName()); - } - } - - sb.append("\n").append(DIVIDER2); - - if(mNetworkStatus != null) - { - sb.append("SERVICES: " + SystemService.toString(mNetworkStatus.getSystemServiceClass())).append("\n"); - sb.append(DIVIDER2); - sb.append("PCCH DOWNLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatus.getDownlinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) - .append("] UPLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatus.getUplinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) - .append("]\n"); - } - else if(mNetworkStatusExtended != null) - { - sb.append("\nSERVICES:").append(SystemService.toString(mNetworkStatusExtended.getSystemServiceClass())) - .append("\n"); - sb.append(DIVIDER2); - sb.append("PCCH DOWNLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatusExtended.getTransmitIdentifier()).append("-") - .append(mNetworkStatusExtended.getTransmitChannel()).append("] UPLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatusExtended.getReceiveIdentifier()).append("-") - .append(mNetworkStatusExtended.getReceiveChannel()).append("]\n"); - } - - if(mSecondaryControlChannels.isEmpty()) - { - sb.append("SCCH: NONE\n"); - } - else - { - for(SecondaryControlChannelBroadcast sec : mSecondaryControlChannels) - { - sb.append("SCCH DOWNLINK ") - .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency1() / 1E6d)) - .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("] UPLINK ") - .append(mFrequencyFormatter.format((double)sec.getUplinkFrequency1() / 1E6d)) - .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("]"); - - if(sec.hasChannel2()) - { - sb.append(" SCCH 2 DOWNLINK ") - .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency2() / 1E6d)) - .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()) - .append("] UPLINK ").append(mFrequencyFormatter.format((double)sec.getUplinkFrequency2() / 1E6d)) - .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()).append("]"); - } - - sb.append("\n"); - } - } - - - if(mSNDCPDataChannel != null) - { - sb.append("SNDCP DOWNLINK ") - .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D)) - .append(" [").append(mSNDCPDataChannel.getTransmitChannel()).append("]").append(" UPLINK ") - .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D)) - .append(" [").append(mSNDCPDataChannel.getReceiveChannel()).append("]\n"); - } - - if(mProtectionParameterBroadcast != null) - { - sb.append(DIVIDER2); - sb.append("ENCRYPTION TYPE:").append(mProtectionParameterBroadcast.getEncryptionType().name()); - sb.append(" ALGORITHM:").append(mProtectionParameterBroadcast.getAlgorithmID()); - sb.append(" KEY:").append(mProtectionParameterBroadcast.getKeyID()); - sb.append(" INBOUND IV:").append(mProtectionParameterBroadcast.getInboundInitializationVector()); - sb.append(" OUTBOUND IV:").append(mProtectionParameterBroadcast.getOutboundInitializationVector()); - sb.append("\n"); - } - - List identifiers = new ArrayList<>(mBands.keySet()); - - Collections.sort(identifiers); - - sb.append(DIVIDER2).append("FREQUENCY BANDS:\n"); - for(Integer id : identifiers) - { - IBandIdentifier band = mBands.get(id); - sb.append(band.toString()).append("\n"); -// sb.append(" ").append(id); -// sb.append(" - BASE: " + mFrequencyFormatter.format( -// (double)band.getBaseFrequency() / 1E6d)); -// sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format( -// (double)band.getChannelSpacing() / 1E6d)); -// sb.append(" UPLINK OFFSET: " + mFrequencyFormatter.format( -// (double)band.getTransmitOffset() / 1E6D)); -// sb.append("\n"); - } - - sb.append(DIVIDER2).append("NEIGHBOR SITES: "); - - if(mNeighborMap.isEmpty()) - { - sb.append("NONE\n"); - } - else - { - for(IAdjacentSite neighbor : mNeighborMap.values()) - { - sb.append("\n"); - sb.append("NAC:").append(((P25Message)neighbor).getNAC()); - sb.append("h SYSTEM:" + neighbor.getSystemID()); - sb.append("h LRA:" + neighbor.getLRA()); - - String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); - sb.append("h RFSS-SITE:" + neighborID); - - sb.append("h "); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(neighborID); - - if(siteAlias != null) - { - sb.append(siteAlias.getName()); - } - } - - sb.append("\n PCCH: DOWNLINK ") - .append(mFrequencyFormatter.format((double)neighbor.getDownlinkFrequency() / 1E6d)) - .append(" [").append(neighbor.getDownlinkChannel()).append("] UPLINK:") - .append(mFrequencyFormatter.format((double)neighbor.getUplinkFrequency() / 1E6d)) - .append(" [").append(neighbor.getDownlinkChannel()).append("] SERVICES: ") - .append(neighbor.getSystemServiceClass()); - } - } - - return sb.toString(); + return mNetworkConfigurationMonitor.getActivitySummary(); } @Override @@ -3554,32 +2396,7 @@ public class P25DecoderState extends DecoderState { case RESET: resetState(); - break; - case SOURCE_FREQUENCY: - mCurrentChannelFrequency = event.getFrequency(); - break; - case TRAFFIC_CHANNEL_ALLOCATION: - if(event.getSource() != P25DecoderState.this) - { - if(event instanceof TrafficChannelAllocationEvent) - { - TrafficChannelAllocationEvent allocationEvent = (TrafficChannelAllocationEvent)event; - - mCurrentCallEvent = (P25CallEvent)allocationEvent.getCallEvent(); - - mCurrentChannel = allocationEvent.getCallEvent().getChannel(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, mCurrentChannel)); - - mCurrentChannelFrequency = allocationEvent.getCallEvent().getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, mCurrentChannelFrequency)); - - mFromTalkgroupMonitor.reset(); - mFromTalkgroupMonitor.process(allocationEvent.getCallEvent().getFromID()); - - mToTalkgroupMonitor.reset(); - mToTalkgroupMonitor.process(allocationEvent.getCallEvent().getToID()); - } - } + mNetworkConfigurationMonitor.reset(); break; default: break; @@ -3596,9 +2413,13 @@ public class P25DecoderState extends DecoderState } } + @Override + public void init() + { + } + @Override public void stop() { - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25Interleave.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25Interleave.java index de6f3231..37e73c01 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25Interleave.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25Interleave.java @@ -1,6 +1,7 @@ package io.github.dsheirer.module.decode.p25; import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,129 +12,152 @@ import java.util.BitSet; */ public class P25Interleave { - private final static Logger mLog = - LoggerFactory.getLogger( P25Interleave.class ); + private final static Logger mLog = + LoggerFactory.getLogger(P25Interleave.class); - private static int[] DATA_INTERLEAVE = new int[] { 0,1,2,3,52,53,54,55,100, - 101,102,103,148,149,150,151,4,5,6,7,56,57,58,59,104,105,106,107,152,153, - 154,155,8,9,10,11,60,61,62,63,108,109,110,111,156,157,158,159,12,13,14, - 15,64,65,66,67,112,113,114,115,160,161,162,163,16,17,18,19,68,69,70,71, - 116,117,118,119,164,165,166,167,20,21,22,23,72,73,74,75,120,121,122,123, - 168,169,170,171,24,25,26,27,76,77,78,79,124,125,126,127,172,173,174,175, - 28,29,30,31,80,81,82,83,128,129,130,131,176,177,178,179,32,33,34,35,84, - 85,86,87,132,133,134,135,180,181,182,183,36,37,38,39,88,89,90,91,136, - 137,138,139,184,185,186,187,40,41,42,43,92,93,94,95,140,141,142,143,188, - 189,190,191,44,45,46,47,96,97,98,99,144,145,146,147,192,193,194,195,48, - 49,50,51 }; + public static int[] DATA_INTERLEAVE = new int[]{0, 1, 2, 3, 52, 53, 54, 55, 100, + 101, 102, 103, 148, 149, 150, 151, 4, 5, 6, 7, 56, 57, 58, 59, 104, 105, 106, 107, 152, 153, + 154, 155, 8, 9, 10, 11, 60, 61, 62, 63, 108, 109, 110, 111, 156, 157, 158, 159, 12, 13, 14, + 15, 64, 65, 66, 67, 112, 113, 114, 115, 160, 161, 162, 163, 16, 17, 18, 19, 68, 69, 70, 71, + 116, 117, 118, 119, 164, 165, 166, 167, 20, 21, 22, 23, 72, 73, 74, 75, 120, 121, 122, 123, + 168, 169, 170, 171, 24, 25, 26, 27, 76, 77, 78, 79, 124, 125, 126, 127, 172, 173, 174, 175, + 28, 29, 30, 31, 80, 81, 82, 83, 128, 129, 130, 131, 176, 177, 178, 179, 32, 33, 34, 35, 84, + 85, 86, 87, 132, 133, 134, 135, 180, 181, 182, 183, 36, 37, 38, 39, 88, 89, 90, 91, 136, + 137, 138, 139, 184, 185, 186, 187, 40, 41, 42, 43, 92, 93, 94, 95, 140, 141, 142, 143, 188, + 189, 190, 191, 44, 45, 46, 47, 96, 97, 98, 99, 144, 145, 146, 147, 192, 193, 194, 195, 48, + 49, 50, 51}; - private static int[] DATA_DEINTERLEAVE = new int[] { 0,1,2,3,16,17,18,19,32, - 33,34,35,48,49,50,51,64,65,66,67,80,81,82,83,96,97,98,99,112,113,114,115, - 128,129,130,131,144,145,146,147,160,161,162,163,176,177,178,179,192,193, - 194,195,4,5,6,7,20,21,22,23,36,37,38,39,52,53,54,55,68,69,70,71,84,85, - 86,87,100,101,102,103,116,117,118,119,132,133,134,135,148,149,150,151, - 164,165,166,167,180,181,182,183,8,9,10,11,24,25,26,27,40,41,42,43,56,57, - 58,59,72,73,74,75,88,89,90,91,104,105,106,107,120,121,122,123,136,137, - 138,139,152,153,154,155,168,169,170,171,184,185,186,187,12,13,14,15,28, - 29,30,31,44,45,46,47,60,61,62,63,76,77,78,79,92,93,94,95,108,109,110, - 111,124,125,126,127,140,141,142,143,156,157,158,159,172,173,174,175,188, - 189,190,191 }; - - private static int[] VOICE_INTERLEAVE = new int[] { 0,24,48,72,96,120,25, - 1,73,49,121,97,2,26,50,74,98,122,27,3,75,51,123,99,4,28,52,76,100,124, - 29,5,77,53,125,101,6,30,54,78,102,126,31,7,79,55,127,103,8,32,56,80,104, - 128,33,9,81,57,129,105,10,34,58,82,106,130,35,11,83,59,131,107,12,36,60, - 84,108,132,37,13,85,61,133,109,14,38,62,86,110,134,39,87,15,63,135,111, - 16,40,64,88,112,136,41,17,89,65,137,113,18,42,66,90,114,138,43,19,91, - 67,139,115,20,44,68,92,116,140,45,21,93,69,141,117,22,46,70,94,119,142, - 47,23,95,71,143,118 }; + public static int[] DATA_DEINTERLEAVE = new int[]{0, 1, 2, 3, 16, 17, 18, 19, 32, + 33, 34, 35, 48, 49, 50, 51, 64, 65, 66, 67, 80, 81, 82, 83, 96, 97, 98, 99, 112, 113, 114, 115, + 128, 129, 130, 131, 144, 145, 146, 147, 160, 161, 162, 163, 176, 177, 178, 179, 192, 193, + 194, 195, 4, 5, 6, 7, 20, 21, 22, 23, 36, 37, 38, 39, 52, 53, 54, 55, 68, 69, 70, 71, 84, 85, + 86, 87, 100, 101, 102, 103, 116, 117, 118, 119, 132, 133, 134, 135, 148, 149, 150, 151, + 164, 165, 166, 167, 180, 181, 182, 183, 8, 9, 10, 11, 24, 25, 26, 27, 40, 41, 42, 43, 56, 57, + 58, 59, 72, 73, 74, 75, 88, 89, 90, 91, 104, 105, 106, 107, 120, 121, 122, 123, 136, 137, + 138, 139, 152, 153, 154, 155, 168, 169, 170, 171, 184, 185, 186, 187, 12, 13, 14, 15, 28, + 29, 30, 31, 44, 45, 46, 47, 60, 61, 62, 63, 76, 77, 78, 79, 92, 93, 94, 95, 108, 109, 110, + 111, 124, 125, 126, 127, 140, 141, 142, 143, 156, 157, 158, 159, 172, 173, 174, 175, 188, + 189, 190, 191}; - private static int[] VOICE_DEINTERLEAVE = new int[] { 0,7,12,19,24,31,36,43, - 48,55,60,67,72,79,84,91,96,103,108,115,120,127,132,139,1,6,13,18,25,30, - 37,42,49,54,61,66,73,78,85,90,97,102,109,114,121,126,133,138,2,9,14,21, - 26,33,38,45,50,57,62,69,74,81,86,93,98,105,110,117,122,129,134,141,3,8, - 15,20,27,32,39,44,51,56,63,68,75,80,87,92,99,104,111,116,123,128,135, - 140,4,11,16,23,28,35,40,47,52,59,64,71,76,83,88,95,100,107,112,119,124, - 131,136,143,5,10,17,22,29,34,41,46,53,58,65,70,77,82,89,94,101,106,113, - 118,125,130,137,142 }; - /** - * Deinterleaves the 196-bit block in message, identified by start and end - * bit positions. Note: end index (exclusive) should be one more than the - * last bit in the block. - * - * @param message - source message to deinterleave - * @param start - starting bit index for the block - * @param end - ending bit index for the block, plus 1 - */ - public static BinaryMessage deinterleaveData( BinaryMessage message, - int start, int end ) - { - return deinterleave( DATA_DEINTERLEAVE, message, start, end ); - } - - public static BinaryMessage deinterleaveVoice( BinaryMessage message, - int start, int end ) - { - return deinterleave( VOICE_DEINTERLEAVE, message, start, end ); - } - - public static BinaryMessage deinterleave( int[] pattern, BinaryMessage message, - int start, int end ) - { - BitSet original = message.get( start, end ); + public static int[] VOICE_INTERLEAVE = new int[]{0, 24, 48, 72, 96, 120, 25, + 1, 73, 49, 121, 97, 2, 26, 50, 74, 98, 122, 27, 3, 75, 51, 123, 99, 4, 28, 52, 76, 100, 124, + 29, 5, 77, 53, 125, 101, 6, 30, 54, 78, 102, 126, 31, 7, 79, 55, 127, 103, 8, 32, 56, 80, 104, + 128, 33, 9, 81, 57, 129, 105, 10, 34, 58, 82, 106, 130, 35, 11, 83, 59, 131, 107, 12, 36, 60, + 84, 108, 132, 37, 13, 85, 61, 133, 109, 14, 38, 62, 86, 110, 134, 39, 87, 15, 63, 135, 111, + 16, 40, 64, 88, 112, 136, 41, 17, 89, 65, 137, 113, 18, 42, 66, 90, 114, 138, 43, 19, 91, + 67, 139, 115, 20, 44, 68, 92, 116, 140, 45, 21, 93, 69, 141, 117, 22, 46, 70, 94, 119, 142, + 47, 23, 95, 71, 143, 118}; - /* Clear block bits in source message */ - message.clear( start, end ); + public static int[] VOICE_DEINTERLEAVE = new int[]{0, 7, 12, 19, 24, 31, 36, 43, + 48, 55, 60, 67, 72, 79, 84, 91, 96, 103, 108, 115, 120, 127, 132, 139, 1, 6, 13, 18, 25, 30, + 37, 42, 49, 54, 61, 66, 73, 78, 85, 90, 97, 102, 109, 114, 121, 126, 133, 138, 2, 9, 14, 21, + 26, 33, 38, 45, 50, 57, 62, 69, 74, 81, 86, 93, 98, 105, 110, 117, 122, 129, 134, 141, 3, 8, + 15, 20, 27, 32, 39, 44, 51, 56, 63, 68, 75, 80, 87, 92, 99, 104, 111, 116, 123, 128, 135, + 140, 4, 11, 16, 23, 28, 35, 40, 47, 52, 59, 64, 71, 76, 83, 88, 95, 100, 107, 112, 119, 124, + 131, 136, 143, 5, 10, 17, 22, 29, 34, 41, 46, 53, 58, 65, 70, 77, 82, 89, 94, 101, 106, 113, + 118, 125, 130, 137, 142}; - /* Iterate only the set bits in the original message and apply - * the deinterleave -- we don't have to evaluate the 0 bits */ - for (int i = original.nextSetBit( 0 ); - i >= 0 && i < pattern.length; - i = original.nextSetBit( i + 1 ) ) - { - message.set( start + pattern[ i ] ); - } - - return message; - } + /** + * Deinterleaves the 196-bit block in message, identified by start and end + * bit positions. Note: end index (exclusive) should be one more than the + * last bit in the block. + * + * @param message - source message to deinterleave + * @param start - starting bit index for the block + * @param end - ending bit index for the block, plus 1 + */ + public static BinaryMessage deinterleaveData(BinaryMessage message, + int start, int end) + { + return deinterleave(DATA_DEINTERLEAVE, message, start, end); + } + + public static BinaryMessage deinterleaveVoice(BinaryMessage message, + int start, int end) + { + return deinterleave(VOICE_DEINTERLEAVE, message, start, end); + } + + public static BinaryMessage deinterleave(int[] pattern, BinaryMessage message, + int start, int end) + { + BitSet original = message.get(start, end); + + /* Clear block bits in source message */ + message.clear(start, end); + + /* Iterate only the set bits in the original message and apply + * the deinterleave -- we don't have to evaluate the 0 bits */ + for(int i = original.nextSetBit(0); + i >= 0 && i < pattern.length; + i = original.nextSetBit(i + 1)) + { + message.set(start + pattern[i]); + } + + return message; + } + + /** + * Deinterleaves the message bits between start and end and returns them in a new message + * @param pattern to use for deinterleaving + * @param interleaved bitset from the original message + * @return binary message with length equal to the pattern size + */ + public static CorrectedBinaryMessage deinterleaveChunk(int[] pattern, BitSet interleaved) + { + CorrectedBinaryMessage deinterleaved = new CorrectedBinaryMessage(pattern.length); + + /* Iterate only the set bits in the original message and apply + * the deinterleave -- we don't have to evaluate the 0 bits */ + for(int i = interleaved.nextSetBit(0); + i >= 0 && i < pattern.length; + i = interleaved.nextSetBit(i + 1)) + { + deinterleaved.set(pattern[i]); + } + + return deinterleaved; + } /** * Interleaves the 196-bit block in message, identified by start and end - * bit positions. Note: end index (exclusive) should be one more than the + * bit positions. Note: end index (exclusive) should be one more than the * last bit in the block. - * + * * @param message - source message to interleave * @param start - starting bit index for the block * @param end - ending bit index for the block, plus 1 */ - public static BinaryMessage interleaveData( BinaryMessage message, - int start, int end ) + public static BinaryMessage interleaveData(BinaryMessage message, + int start, int end) { - return interleave( DATA_INTERLEAVE, message, start, end ); + return interleave(DATA_INTERLEAVE, message, start, end); } - - public static BinaryMessage interleaveVoice( BinaryMessage message, - int start, int end ) + + public static BinaryMessage interleaveVoice(BinaryMessage message, + int start, int end) { - return interleave( VOICE_INTERLEAVE, message, start, end ); + return interleave(VOICE_INTERLEAVE, message, start, end); } - - public static BinaryMessage interleave( int[] pattern, BinaryMessage message, - int start, int end ) + + public static BinaryMessage interleave(int[] pattern, BinaryMessage message, + int start, int end) { - BitSet original = message.get( start, end ); - - /* Clear block bits in source message */ - message.clear( start, end ); + BitSet original = message.get(start, end); + + /* Clear block bits in source message */ + message.clear(start, end); /* Iterate only the set bits in the original message and apply * the deinterleave -- we don't have to evaluate the 0 bits */ - for (int i = original.nextSetBit( 0 ); - i >= 0 && i < pattern.length; - i = original.nextSetBit( i + 1 ) ) + for(int i = original.nextSetBit(0); + i >= 0 && i < pattern.length; + i = original.nextSetBit(i + 1)) { - message.set( start + pattern[ i ] ); + message.set(start + pattern[i]); } - + return message; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java deleted file mode 100644 index 54e00ea9..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java +++ /dev/null @@ -1,744 +0,0 @@ -package io.github.dsheirer.module.decode.p25; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.bits.BitSetFullException; -import io.github.dsheirer.bits.MultiSyncPatternMatcher; -import io.github.dsheirer.bits.SoftSyncDetector; -import io.github.dsheirer.bits.SyncDetector; -import io.github.dsheirer.dsp.psk.pll.IPhaseLockedLoop; -import io.github.dsheirer.dsp.symbol.Dibit; -import io.github.dsheirer.dsp.symbol.FrameSync; -import io.github.dsheirer.dsp.symbol.ISyncDetectListener; -import io.github.dsheirer.edac.BCH_63_16_11; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.hdu.HDUMessage; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU2Message; -import io.github.dsheirer.module.decode.p25.message.ldu.lc.LDULCMessageFactory; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessageFactory; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import io.github.dsheirer.module.decode.p25.message.pdu.confirmed.PDUConfirmedMessageFactory; -import io.github.dsheirer.module.decode.p25.message.tdu.TDUMessage; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.TDULCMessageFactory; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.TDULinkControlMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessageFactory; -import io.github.dsheirer.module.decode.p25.message.vselp.VSELP1Message; -import io.github.dsheirer.module.decode.p25.message.vselp.VSELP2Message; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.sample.Listener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class P25MessageFramer implements Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(P25MessageFramer.class); - - /* Determines the threshold for sync pattern soft matching */ - private static final int SYNC_MATCH_THRESHOLD = 2; - private static final int SYNC_IN_CALL_THRESHOLD = 4; - - /* Costas Loop phase lock error correction values. A phase lock error of - * 90 degrees requires a correction of 1/4 of the symbol rate (1200Hz). An - * error of 180 degrees requires a correction of 1/2 of the symbol rate */ - public static final double DEFAULT_SAMPLE_RATE = 25000.0; - public static final double FREQUENCY_PHASE_CORRECTION_90_DEGREES = 1200.0; - public static final double FREQUENCY_PHASE_CORRECTION_180_DEGREES = 2400.0; - - public static final int TSBK_BEGIN = 64; - public static final int TSBK_CRC_START = 144; - public static final int TSBK_END = 260; - public static final int TSBK_DECODED_END = 160; - - public static final int PDU0_BEGIN = 64; - public static final int PDU0_CRC_BEGIN = 144; - public static final int PDU0_END = 260; - public static final int PDU1_BEGIN = 160; - public static final int PDU1_END = 356; - public static final int PDU2_BEGIN = 256; - public static final int PDU2_END = 452; - public static final int PDU3_BEGIN = 352; - public static final int PDU3_END = 548; - public static final int PDU3_DECODED_END = 448; - - private SoftSyncDetector mPrimarySyncDetector = new SoftSyncDetector( - FrameSync.P25_PHASE1_NORMAL.getSync(), SYNC_MATCH_THRESHOLD); - - private PLLPhaseInversionDetector mInversionDetector90CW; - private PLLPhaseInversionDetector mInversionDetector90CCW; - private PLLPhaseInversionDetector mInversionDetector180; - - private MultiSyncPatternMatcher mMatcher; - private P25MessageAssembler mMessageAssembler = new P25MessageAssembler(); - - private Listener mListener; - private AliasList mAliasList; - - private Trellis_1_2_Rate mHalfRate = new Trellis_1_2_Rate(); - private Trellis_3_4_Rate mThreeQuarterRate = new Trellis_3_4_Rate(); - private BCH_63_16_11 mNIDDecoder = new BCH_63_16_11(); - - /** - * Constructs a P25 message framer to receive a stream of symbols and - * detect the sync pattern then capture the following stream of symbols up - * to the message length, and then broadcast that bit buffer to the registered - * listener. - */ - public P25MessageFramer(AliasList aliasList, ISyncDetectListener syncDetectListener) - { - mAliasList = aliasList; - - //Assign the sync detect listener to the matcher with a sync loss threshold equal to the longest message length - mMatcher = new MultiSyncPatternMatcher(syncDetectListener, DataUnitID.LDU1.getMessageLength(), 48); - - mPrimarySyncDetector.setListener(new ISyncDetectListener() - { - @Override - public void syncDetected() - { - if(!mMessageAssembler.isActive()) - { - mMessageAssembler.setActive(true); - } - } - - @Override - public void syncLost() - { - //no-op - } - }); - - mMatcher.add(mPrimarySyncDetector); - } - - public P25MessageFramer(AliasList aliasList, IPhaseLockedLoop phaseLockedLoop, ISyncDetectListener syncDetectListener) - { - this(aliasList, syncDetectListener); - - if(phaseLockedLoop != null) - { - //Add additional sync pattern detectors to detect when we get 90/180 degree out of phase sync pattern - //detections so that we can apply correction to the phase locked loop - mInversionDetector90CW = new PLLPhaseInversionDetector(FrameSync.P25_PHASE1_ERROR_90_CW, - phaseLockedLoop, DEFAULT_SAMPLE_RATE, FREQUENCY_PHASE_CORRECTION_90_DEGREES); - mMatcher.add(mInversionDetector90CW); - - mInversionDetector90CCW = new PLLPhaseInversionDetector(FrameSync.P25_PHASE1_ERROR_90_CCW, - phaseLockedLoop, DEFAULT_SAMPLE_RATE, -FREQUENCY_PHASE_CORRECTION_90_DEGREES); - mMatcher.add(mInversionDetector90CCW); - - mInversionDetector180 = new PLLPhaseInversionDetector(FrameSync.P25_PHASE1_ERROR_180, - phaseLockedLoop, DEFAULT_SAMPLE_RATE, FREQUENCY_PHASE_CORRECTION_180_DEGREES); - mMatcher.add(mInversionDetector180); - } - } - - /** - * Updates the incoming sample stream sample rate to allow the PLL phase inversion detectors to - * recalculate their internal phase correction values. - * - * @param sampleRate of the incoming sample stream - */ - public void setSampleRate(double sampleRate) - { - mInversionDetector180.setSampleRate(sampleRate); - mInversionDetector90CW.setSampleRate(sampleRate); - mInversionDetector90CCW.setSampleRate(sampleRate); - } - - public void dispose() - { - mMessageAssembler.dispose(); - - mListener = null; - mAliasList = null; - - mMatcher.dispose(); - mMatcher = null; - - mPrimarySyncDetector.dispose(); - mPrimarySyncDetector = null; - - } - - private void dispatch(Message message) - { - if(mListener != null) - { - mListener.receive(message); - } - } - - @Override - public void receive(Dibit symbol) - { - if(mMessageAssembler.isActive()) - { - mMessageAssembler.receive(symbol); - - if(mMessageAssembler.complete()) - { - mMessageAssembler.reset(); - } - } - - mMatcher.receive(symbol.getBit1(), symbol.getBit2()); - } - - public void setListener(Listener listener) - { - mListener = listener; - } - - public void removeListener(Listener listener) - { - mListener = null; - } - - private class P25MessageAssembler - { - /* Starting position of the status symbol counter is 24 symbols to - * account for the 48-bit sync pattern which is not included in message */ - private int mStatusSymbolPointer = 24; - private BinaryMessage mMessage; - private int mMessageLength; - private boolean mComplete = false; - private boolean mActive = false; - private DataUnitID mDUID = DataUnitID.NID; - - public P25MessageAssembler() - { - mMessageLength = mDUID.getMessageLength(); - mMessage = new BinaryMessage(mMessageLength); - reset(); - } - - public void receive(Dibit dibit) - { - if(mActive) - { - if(mStatusSymbolPointer == 35) - { - mStatusSymbolPointer = 0; - } - else - { - mStatusSymbolPointer++; - - try - { - mMessage.add(dibit.getBit1()); - mMessage.add(dibit.getBit2()); - } - catch(BitSetFullException e) - { - mComplete = true; - } - - /* Check the message for complete */ - if(mMessage.isFull()) - { - checkComplete(); - } - } - } - } - - public void reset() - { - mDUID = DataUnitID.NID; - mMessage.setSize(mDUID.getMessageLength()); - mMessage.clear(); - mStatusSymbolPointer = 24; - mComplete = false; - mActive = false; - } - - public void setActive(boolean active) - { - mActive = active; - } - - private void setDUID(DataUnitID id) - { - mDUID = id; - mMessageLength = id.getMessageLength(); - mMessage.setSize(mMessageLength); - } - - private void checkComplete() - { - switch(mDUID) - { - case NID: - mMessage = mNIDDecoder.correctNID(mMessage); - - if(mMessage.getCRC() != CRC.FAILED_CRC) - { - int value = mMessage.getInt(P25Message.DUID); - - DataUnitID duid = DataUnitID.fromValue(value); - - if(duid != DataUnitID.UNKN) - { - setDUID(duid); - } - else - { - mComplete = true; - dispatch(new P25Message(mMessage.copy(), mDUID, mAliasList)); - } - } - else - { - mComplete = true; - } - break; - case HDU: - mComplete = true; - dispatch(new HDUMessage(mMessage.copy(), mDUID, mAliasList)); - - /* We're in a call now, lower the sync match threshold */ - mPrimarySyncDetector.setThreshold(SYNC_IN_CALL_THRESHOLD); - break; - case LDU1: - mComplete = true; - - LDU1Message ldu1 = new LDU1Message(mMessage.copy(), - mDUID, mAliasList); - - /* Convert the LDU1 message into a link control LDU1 message */ - dispatch(LDULCMessageFactory.getMessage(ldu1)); - - /* We're in a call now, lower the sync match threshold */ - mPrimarySyncDetector.setThreshold(SYNC_IN_CALL_THRESHOLD); - break; - case LDU2: - mComplete = true; - dispatch(new LDU2Message(mMessage.copy(), mDUID, mAliasList)); - - /* We're in a call now, lower the sync match threshold */ - mPrimarySyncDetector.setThreshold(SYNC_IN_CALL_THRESHOLD); - break; - case PDU0: - - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, PDU0_BEGIN, PDU0_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - if(mHalfRate.decode(mMessage, PDU0_BEGIN, PDU0_END)) - { - mMessage = CRCP25.correctCCITT80(mMessage, - PDU0_BEGIN, PDU0_CRC_BEGIN); - - - if(mMessage.getCRC() != CRC.FAILED_CRC) - { - boolean confirmed = mMessage.get( - PDUMessage.CONFIRMATION_REQUIRED_INDICATOR); - - if(confirmed) - { - setDUID(DataUnitID.PDUC); - } - else - { - setDUID(DataUnitID.PDU1); - } - - mMessage.setPointer(PDU1_BEGIN); - } - else - { - mComplete = true; - } - } - else - { - mComplete = true; - } - - /* Set sync match threshold to normal */ - mPrimarySyncDetector.setThreshold(SYNC_MATCH_THRESHOLD); - - break; - case PDU1: - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, PDU1_BEGIN, PDU1_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - if(mHalfRate.decode(mMessage, PDU1_BEGIN, PDU1_END)) - { - if(mMessage.getInt(PDUMessage.BLOCKS_TO_FOLLOW) == 1) - { - mMessage.setSize(PDU2_BEGIN); - - PDUMessage pduMessage1 = PDUMessageFactory.getMessage( - mMessage.copy(), DataUnitID.PDU1, mAliasList); - - dispatch(pduMessage1); - - mComplete = true; - } - else - { - setDUID(DataUnitID.PDU2); - - mMessage.setPointer(PDU2_BEGIN); - } - } - else - { - mComplete = true; - } - break; - case PDU2: - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, PDU2_BEGIN, PDU2_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - if(mHalfRate.decode(mMessage, PDU2_BEGIN, PDU2_END)) - { - if(mMessage.getInt(PDUMessage.BLOCKS_TO_FOLLOW) == 2) - { - mMessage.setSize(PDU3_BEGIN); - - PDUMessage pduMessage2 = PDUMessageFactory.getMessage( - mMessage.copy(), DataUnitID.PDU2, mAliasList); - - dispatch(pduMessage2); - - mComplete = true; - } - else - { - setDUID(DataUnitID.PDU3); - - mMessage.setPointer(PDU3_BEGIN); - } - } - else - { - mComplete = true; - } - - /* Set sync match threshold to normal */ - mPrimarySyncDetector.setThreshold(SYNC_MATCH_THRESHOLD); - break; - case PDU3: - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, PDU3_BEGIN, PDU3_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - if(mHalfRate.decode(mMessage, PDU3_BEGIN, PDU3_END)) - { - mMessage.setSize(PDU3_DECODED_END); - - PDUMessage pduMessage3 = PDUMessageFactory.getMessage(mMessage.copy(), DataUnitID.PDU3, mAliasList); - - dispatch(pduMessage3); - } - - mComplete = true; - break; - case PDUC: - /* De-interleave the latest block*/ - P25Interleave.deinterleaveData(mMessage, - mMessage.size() - 196, mMessage.size()); - - /* Decode 3/4 rate convolutional encoding from latest block */ - if(mThreeQuarterRate.decode(mMessage, - mMessage.size() - 196, mMessage.size())) - { - /* Resize the message and adjust the message pointer - * to account for removing 48 + 4 parity bits */ - mMessage.setSize(mMessage.size() - 52); - mMessage.adjustPointer(-52); - - int blocks = mMessage.getInt(PDUMessage.BLOCKS_TO_FOLLOW); - - int current = (mMessage.size() - 160) / 144; - - if(current < blocks) - { - mMessage.setSize(mMessage.size() + 196); - mMessageLength = mMessage.size(); - } - else - { - PDUConfirmedMessage pducm = - new PDUConfirmedMessage(mMessage.copy(), - mAliasList); - - /* Translate into correct subclass */ - dispatch(PDUConfirmedMessageFactory.getMessage(pducm)); - - mComplete = true; - } - break; - } - else - { - PDUConfirmedMessage pducm = - new PDUConfirmedMessage(mMessage.copy(), - mAliasList); - - /* Translate into correct subclass */ - dispatch(PDUConfirmedMessageFactory.getMessage(pducm)); - - mComplete = true; - } - - /* Set sync match threshold to normal */ - mPrimarySyncDetector.setThreshold(SYNC_MATCH_THRESHOLD); - break; - case TDU: - dispatch(new TDUMessage(mMessage.copy(), mDUID, mAliasList)); - mComplete = true; - - /* Set sync match threshold to normal */ - mPrimarySyncDetector.setThreshold(SYNC_MATCH_THRESHOLD); - break; - case TDULC: - TDULinkControlMessage tdulc = new TDULinkControlMessage( - mMessage.copy(), mDUID, mAliasList); - - /* Convert to an appropriate link control message */ - tdulc = TDULCMessageFactory.getMessage(tdulc); - - dispatch(tdulc); - mComplete = true; - - /* Set sync match threshold to normal */ - mPrimarySyncDetector.setThreshold(SYNC_MATCH_THRESHOLD); - break; - case TSBK1: - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, TSBK_BEGIN, TSBK_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - - if(mHalfRate.decode(mMessage, TSBK_BEGIN, TSBK_END)) - { - mMessage = CRCP25.correctCCITT80(mMessage, - TSBK_BEGIN, TSBK_CRC_START); - - if(mMessage.getCRC() != CRC.FAILED_CRC) - { - BinaryMessage tsbkBuffer1 = mMessage.copy(); - tsbkBuffer1.setSize(TSBK_DECODED_END); - - TSBKMessage tsbkMessage1 = TSBKMessageFactory.getMessage( - tsbkBuffer1, DataUnitID.TSBK1, mAliasList); - - if(tsbkMessage1.isLastBlock()) - { - mComplete = true; - } - else - { - setDUID(DataUnitID.TSBK2); - mMessage.setPointer(TSBK_BEGIN); - } - - dispatch(tsbkMessage1); - } - else - { - mComplete = true; - } - } - else - { - mComplete = true; - } - - /* Set sync match threshold to normal */ - mPrimarySyncDetector.setThreshold(SYNC_MATCH_THRESHOLD); - break; - case TSBK2: - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, TSBK_BEGIN, TSBK_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - if(mHalfRate.decode(mMessage, TSBK_BEGIN, TSBK_END)) - { - mMessage = CRCP25.correctCCITT80(mMessage, - TSBK_BEGIN, TSBK_CRC_START); - - if(mMessage.getCRC() != CRC.FAILED_CRC) - { - BinaryMessage tsbkBuffer2 = mMessage.copy(); - tsbkBuffer2.setSize(TSBK_DECODED_END); - - TSBKMessage tsbkMessage2 = TSBKMessageFactory.getMessage( - tsbkBuffer2, DataUnitID.TSBK2, mAliasList); - - if(tsbkMessage2.isLastBlock()) - { - mComplete = true; - } - else - { - setDUID(DataUnitID.TSBK3); - mMessage.setPointer(TSBK_BEGIN); - } - - dispatch(tsbkMessage2); - } - else - { - mComplete = true; - } - } - else - { - mComplete = true; - } - break; - case TSBK3: - /* Remove interleaving */ - P25Interleave.deinterleaveData(mMessage, TSBK_BEGIN, TSBK_END); - - /* Remove trellis encoding - abort processing if we have an - * unsuccessful decode due to excessive errors */ - if(mHalfRate.decode(mMessage, TSBK_BEGIN, TSBK_END)) - { - mMessage = CRCP25.correctCCITT80(mMessage, - TSBK_BEGIN, TSBK_CRC_START); - - if(mMessage.getCRC() != CRC.FAILED_CRC) - { - BinaryMessage tsbkBuffer3 = mMessage.copy(); - tsbkBuffer3.setSize(TSBK_DECODED_END); - - TSBKMessage tsbkMessage3 = TSBKMessageFactory.getMessage( - tsbkBuffer3, DataUnitID.TSBK3, mAliasList); - - dispatch(tsbkMessage3); - } - } - - mComplete = true; - break; - case VSELP1: - mComplete = true; - dispatch(new VSELP1Message(mMessage.copy(), mDUID, mAliasList)); - break; - case VSELP2: - mComplete = true; - dispatch(new VSELP2Message(mMessage.copy(), mDUID, mAliasList)); - break; - case UNKN: - mComplete = true; - dispatch(new P25Message(mMessage.copy(), mDUID, mAliasList)); - break; - default: - mComplete = true; - break; - } - } - - public void dispose() - { - mMessage = null; - mHalfRate.dispose(); - } - - /** - * Flag to indicate when this assembler has received all of the bits it - * is looking for (ie message length), and should then be removed from - * receiving any more bits - */ - public boolean complete() - { - return mComplete; - } - - public boolean isActive() - { - return mActive; - } - } - - /** - * Sync pattern detector to listen for costas loop phase lock errors and apply a phase correction to the costas - * loop so that we don't miss any messages. - * - * When the costas loop locks with a +/- 90 degree or 180 degree phase error, the slicer will incorrectly apply - * the symbol pattern rotated left or right by the phase error. However, we can detect these rotated sync patterns - * and apply immediate phase correction so that message processing can continue. - */ - public class PLLPhaseInversionDetector extends SyncDetector - { - private FrameSync mFrameSync; - private IPhaseLockedLoop mPhaseLockedLoop; - private double mSampleRate; - private double mFrequencyCorrection; - private double mPllCorrection; - - /** - * Constructs the PLL phase inversion detector. - * - * @param frameSync pattern to monitor for detecting phase inversion errors - * @param phaseLockedLoop to receive phase correction values - * @param sampleRate of the incoming sample stream - * @param frequencyCorrection to apply to the PLL. Examples: - * QPSK +/-90 degree correction: +/-SYMBOL RATE / 4.0 - * QPSK 180 degree correction: SYMBOL RATE / 2.0 - */ - public PLLPhaseInversionDetector(FrameSync frameSync, IPhaseLockedLoop phaseLockedLoop, double sampleRate, - double frequencyCorrection) - { - super(frameSync.getSync()); - mFrameSync = frameSync; - mPhaseLockedLoop = phaseLockedLoop; - mFrequencyCorrection = frequencyCorrection; - setSampleRate(sampleRate); - - setListener(new ISyncDetectListener() - { - @Override - public void syncDetected() - { - mPhaseLockedLoop.correctInversion(mPllCorrection); - - /* Since we detected a sync pattern, start a message assembler */ - if(!mMessageAssembler.isActive()) - { - mMessageAssembler.setActive(true); - } - } - - @Override - public void syncLost() - { - //no-op - } - }); - - } - - /** - * Sets or adjusts the sample rate so that the phase inversion correction value can be recalculated. - * @param sampleRate - */ - public void setSampleRate(double sampleRate) - { - mSampleRate = sampleRate; - mPllCorrection = 2.0 * Math.PI * mFrequencyCorrection / mSampleRate; - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer2.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer2.java new file mode 100644 index 00000000..a830565a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer2.java @@ -0,0 +1,549 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.bits.BitSetFullException; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +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.Message; +import io.github.dsheirer.message.SyncLossMessage; +import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.message.P25MessageFactory; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessageFactory; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.UMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessageFactory; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.protocol.Protocol; +import io.github.dsheirer.record.binary.BinaryReader; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.buffer.ReusableByteBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * P25 Sync Detector and Message Framer. Includes capability to detect PLL out-of-phase lock errors + * and issue phase corrections. + */ +public class P25MessageFramer2 implements Listener, IDataUnitDetectListener +{ + private final static Logger mLog = LoggerFactory.getLogger(P25MessageFramer2.class); + + private P25DataUnitDetector mDataUnitDetector; + private P25ChannelStatusProcessor mChannelStatusProcessor = new P25ChannelStatusProcessor(); + private Listener mMessageListener; + + private boolean mAssemblingMessage = false; + private CorrectedBinaryMessage mBinaryMessage; + private DataUnitID mDataUnitID; + private PDUSequence mPDUSequence; + private int[] mCorrectedNID; + private int mNAC; + private int mStatusSymbolDibitCounter = 0; + private int mTrailingDibitsToSuppress = 0; + private double mBitRate; + private long mCurrentTime = System.currentTimeMillis(); + private ISyncDetectListener mSyncDetectListener; + + public P25MessageFramer2(IPhaseLockedLoop phaseLockedLoop, int bitRate) + { + mDataUnitDetector = new P25DataUnitDetector(this, phaseLockedLoop); + mBitRate = bitRate; + } + + public P25MessageFramer2(int bitRate) + { + this(null, bitRate); + } + + /** + * Sets the sample rate for the sync detector + */ + public void setSampleRate(double sampleRate) + { + mDataUnitDetector.setSampleRate(sampleRate); + } + + /** + * Registers a sync detect listener to be notified each time a sync pattern and NID are detected. + */ + public void setSyncDetectListener(ISyncDetectListener syncDetectListener) + { + mSyncDetectListener = syncDetectListener; + } + + /** + * Current timestamp or timestamp of incoming message buffers that is continuously updated to as + * close as possible to the bits processed for the expected baud rate. + * + * @return + */ + private long getTimestamp() + { + return mCurrentTime; + } + + /** + * Sets the current time. This should be invoked by an incoming message buffer stream. + * + * @param currentTime + */ + public void setCurrentTime(long currentTime) + { + mCurrentTime = currentTime; + } + + /** + * Updates the current timestamp based on the number of bits processed versus the bit rate per second + * in order to keep an accurate running timestamp to use for timestamped message creation. + * + * @param bitsProcessed thus far + */ + private void updateBitsProcessed(int bitsProcessed) + { + if(bitsProcessed > 0) + { + mCurrentTime += (long)((double)bitsProcessed / mBitRate * 1000.0); + } + } + + /** + * Registers the listener for messages produced by this message framer + * + * @param messageListener to receive framed and decoded messages + */ + public void setListener(Listener messageListener) + { + mMessageListener = messageListener; + } + + public P25DataUnitDetector getDataUnitDetector() + { + return mDataUnitDetector; + } + + /** + * Primary method for streaming decoded symbol dibits for message framing. + * + * @param dibit to process + */ + @Override + public void receive(Dibit dibit) + { + if(mAssemblingMessage) + { + //Strip out the status symbol dibit after every 70 bits or 35 dibits + if(mStatusSymbolDibitCounter == 35) + { + if(mAssemblingMessage) + { + //Send status dibit to channel status processor to identify ISP or OSP channel + mChannelStatusProcessor.receive(dibit); + } + mStatusSymbolDibitCounter = 0; + + return; + } + + mStatusSymbolDibitCounter++; + + try + { + mBinaryMessage.add(dibit.getBit1()); + mBinaryMessage.add(dibit.getBit2()); + } + catch(BitSetFullException bsfe) + { + mLog.debug("Message full exception - unexpected"); + + //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 + { + //Suppress any trailing nulls or status dibits that follow certain DUID sequences + if(mTrailingDibitsToSuppress > 0) + { + mTrailingDibitsToSuppress--; + updateBitsProcessed(2); + return; + } + + mDataUnitDetector.receive(dibit); + } + } + + private void dispatchMessage() + { + if(mMessageListener != null) + { + switch(mDataUnitID) + { + case PACKET_HEADER_DATA_UNIT: + mPDUSequence = PDUMessageFactory.createPacketSequence(mNAC, mCurrentTime, mBinaryMessage); + + if(mPDUSequence != null) + { + if(mPDUSequence.getHeader().isValid() && + mPDUSequence.getHeader().getBlocksToFollowCount() > 0) + { + //Setup to catch the sequence of data blocks that follow the header + mDataUnitID = DataUnitID.PACKET_DATA_UNIT; + mBinaryMessage = new CorrectedBinaryMessage(DataUnitID.PACKET_DATA_UNIT.getMessageLength()); + mAssemblingMessage = true; + } + else + { + //Process 44 bits/22 dibits of trailing nulls + mTrailingDibitsToSuppress = 22; + + mMessageListener.receive(PDUMessageFactory.create(mPDUSequence, mNAC, getTimestamp())); + reset(mPDUSequence.getBitsProcessedCount()); + mPDUSequence = null; + } + } + break; + case PACKET_DATA_UNIT: + if(mPDUSequence != null) + { + if(mPDUSequence.getHeader().isConfirmationRequired()) + { + mPDUSequence.addDataBlock(PDUMessageFactory.createConfirmedDataBlock(mBinaryMessage)); + } + else + { + mPDUSequence.addDataBlock(PDUMessageFactory.createUnconfirmedDataBlock(mBinaryMessage)); + } + + if(mPDUSequence.isComplete()) + { + mMessageListener.receive(PDUMessageFactory.create(mPDUSequence, mNAC, getTimestamp())); + + switch(mPDUSequence.getHeader().getBlocksToFollowCount()) + { + case 1: + mTrailingDibitsToSuppress = 29; + break; + case 2: + //Process 2 bits or 1 dibit of trailing nulls + mTrailingDibitsToSuppress = 1; + break; + case 3: + //Process 16 bits or 8 dibits of trailing nulls + mTrailingDibitsToSuppress = 8; + break; + case 4: + //Process 30 bits or 15 dibits of trailing nulls + mTrailingDibitsToSuppress = 15; + break; + case 5: + //Process 44 bits or 22 dibits of trailing nulls + mTrailingDibitsToSuppress = 22; + break; + default: + mLog.debug("*** MORE THAN 5 PDU BLOCKS DETECTED [" + + mPDUSequence.getHeader().getBlocksToFollowCount() + + "] - DETERMINE TRAILING NULL COUNT TO SUPPRESS AND UPDATE CODE"); + break; + } + + reset(mPDUSequence.getBitsProcessedCount()); + } + else + { + //Setup to catch the next data block + mDataUnitID = DataUnitID.PACKET_DATA_UNIT; + mBinaryMessage = new CorrectedBinaryMessage(DataUnitID.PACKET_DATA_UNIT.getMessageLength()); + mAssemblingMessage = true; + } + } + else + { + mLog.error("Received PDU data block with out a preceeding data header"); + reset(mDataUnitID.getMessageLength()); + } + break; + case TRUNKING_SIGNALING_BLOCK_1: + case TRUNKING_SIGNALING_BLOCK_2: + case TRUNKING_SIGNALING_BLOCK_3: + TSBKMessage tsbkMessage = TSBKMessageFactory.create(mChannelStatusProcessor.getDirection(), + mDataUnitID, mBinaryMessage, mNAC, getTimestamp()); + + mMessageListener.receive(tsbkMessage); + + if(tsbkMessage.isLastBlock()) + { + reset(mDataUnitID.getMessageLength()); + mTrailingDibitsToSuppress = 1; + } + else + { + updateBitsProcessed(mDataUnitID.getMessageLength()); + mBinaryMessage = new CorrectedBinaryMessage(mDataUnitID.getMessageLength()); + if(mDataUnitID == DataUnitID.TRUNKING_SIGNALING_BLOCK_1) + { + mDataUnitID = DataUnitID.TRUNKING_SIGNALING_BLOCK_2; + } + else if(mDataUnitID == DataUnitID.TRUNKING_SIGNALING_BLOCK_2) + { + mDataUnitID = DataUnitID.TRUNKING_SIGNALING_BLOCK_3; + } + } + break; + default: + P25Message message = P25MessageFactory.create(mDataUnitID, mNAC, getTimestamp(), mBinaryMessage); + mMessageListener.receive(message); + reset(mDataUnitID.getMessageLength()); + break; + } + } + else + { + reset(0); + } + } + + private void reset(int bitsProcessed) + { + updateBitsProcessed(bitsProcessed); + mPDUSequence = null; + mBinaryMessage = null; + mAssemblingMessage = false; + mDataUnitID = null; + mNAC = 0; + mDataUnitDetector.reset(); + mStatusSymbolDibitCounter = 0; + } + + /** + * Primary method for streaming decoded symbol byte arrays. + * + * @param buffer to process into a stream of dibits for processing. + */ + public void receive(ReusableByteBuffer buffer) + { + //Updates current timestamp to the timestamp from the incoming buffer + setCurrentTime(buffer.getTimestamp()); + + for(byte value : buffer.getBytes()) + { + for(int x = 0; x <= 3; x++) + { + receive(Dibit.parse(value, x)); + } + } + + buffer.decrementUserCount(); + } + + @Override + public void dataUnitDetected(DataUnitID dataUnitID, int nac, int bitErrors, int discardedDibits, int[] correctedNid) + { + if(discardedDibits > 0) + { + dispatchSyncLoss(discardedDibits * 2); + } + + if(dataUnitID.getMessageLength() < 0) + { + dispatchSyncLoss(112); //Sync (48) and Nid (64) + return; + } + + if(mSyncDetectListener != null) + { + mSyncDetectListener.syncDetected(bitErrors); + } + + mDataUnitID = dataUnitID; + mNAC = nac; + mCorrectedNID = correctedNid; + mBinaryMessage = new CorrectedBinaryMessage(dataUnitID.getMessageLength()); + mBinaryMessage.incrementCorrectedBitCount(bitErrors); + + mAssemblingMessage = true; + mStatusSymbolDibitCounter = 21; + } + + @Override + public void syncLost(int bitsProcessed) + { + dispatchSyncLoss(bitsProcessed); + + if(mSyncDetectListener != null) + { + mSyncDetectListener.syncLost(); + } + } + + private void dispatchSyncLoss(int bitsProcessed) + { + //Updates current timestamp according to the number of bits procesed + updateBitsProcessed(bitsProcessed); + + if(bitsProcessed > 0 && mMessageListener != null) + { + mMessageListener.receive(new SyncLossMessage(getTimestamp(), bitsProcessed, Protocol.APCO25)); + } + } + + public static void main(String[] args) + { + boolean pduOnly = false; + boolean mbtcOnly = false; + boolean sndcpOnly = false; + boolean ippacketOnly = false; + boolean patchOnly = false; + + P25MessageFramer2 messageFramer = new P25MessageFramer2(null, DecoderType.P25_PHASE1.getProtocol().getBitRate()); + messageFramer.setListener(new Listener() + { + @Override + public void receive(Message message) + { + if(mbtcOnly) + { + if(message instanceof AMBTCMessage || message instanceof UMBTCMessage) + { + mLog.debug(message.toString()); + } + } + else if(pduOnly) + { + String s = message.toString(); + + if(s.contains(" PDU ")) + { + mLog.debug(s); + } + } + else if(sndcpOnly) + { + String s = message.toString(); + + if(s.contains("SNDCP")) + { + mLog.debug(s); + } + } + else if(ippacketOnly) + { + String s = message.toString(); + + if(s.contains("IPPKT") || s.contains("SNDCP") || s.contains(" PDU ")) + { + mLog.debug(s); + } + } + else if(patchOnly) + { + if(message instanceof PatchGroupVoiceChannelGrant || message instanceof PatchGroupVoiceChannelGrantUpdate) + { + mLog.debug(message.toString()); + } + } + else + { + String a = message.toString(); + mLog.debug(a); + } + } + }); + +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180922_040600_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180922_042033_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_045614_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); //Has Moto LC Opcode 01 +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_045647_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050325_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050403_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050721_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_051057_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); //ARS PDUs and SNDCP PDUs +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_051808_9600BPS_CNYICC_Onondaga Simulcast_LCN 09_EMERG_AND_PACKETS.bits"); //ARS and SNCDP PDUs +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_052519_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181008_091511_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181008_091650_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101745_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101850_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101855_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102145_9600BPS_CNYICC_Onondaga Simulcast_LCN 04.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102451_9600BPS_CNYICC_Onondaga Simulcast_LCN 09_lots_of_PACKET_DATA.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102456_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102459_9600BPS_CNYICC_Onondaga Simulcast_LCN 11.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102501_9600BPS_CNYICC_Onondaga Simulcast_LCN 12.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_103208_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_140834_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_140851_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_141547_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_141607_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_142300_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_142319_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143012_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143031_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143723_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143741_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_100905_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_101622_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_102339_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_134948_9600BPS_CNYICC_Oswego Simulcast_LCN 04.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_144312_9600BPS_CNYICC_Oswego Simulcast_LCN 04.bits"); //Interesting UDP port 231 packets (oswego LCN 4) +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_144429_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); +// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_144437_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); + Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181202_064827_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits"); + + + + try(BinaryReader reader = new BinaryReader(path, 200)) + { + while(reader.hasNext()) + { + messageFramer.receive(reader.next()); + } + } + catch(Exception ioe) + { + ioe.printStackTrace(); + } + + + mLog.debug("NIDS Detected: " + messageFramer.getDataUnitDetector().getNIDDetectionCount()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java index c6e5c6be..aefb846b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java @@ -15,95 +15,94 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.p25; -import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.Message; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDUMessage; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; +import java.util.List; +import java.util.Map; public class P25MessageProcessor implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( P25MessageProcessor.class ); + private final static Logger mLog = LoggerFactory.getLogger(P25MessageProcessor.class); - private Listener mMessageListener; + private Listener mMessageListener; - /* Map of up to 16 band identifiers per RFSS. These identifier update - * messages are inserted into any message that conveys channel information - * so that the uplink/downlink frequencies can be calculated */ - private HashMap mBandIdentifierMap = - new HashMap(); - - private AliasList mAliasList; - - public P25MessageProcessor( AliasList aliasList ) - { - mAliasList = aliasList; - } - - @Override - public void receive( Message message ) + /* Map of up to 16 band identifiers per RFSS. These identifier update + * messages are inserted into any message that conveys channel information + * so that the uplink/downlink frequencies can be calculated */ + private Map mFrequencyBandMap = new HashMap(); + + public P25MessageProcessor() { - /** - * Capture frequency band identifier messages and inject them into any - * messages that require band information in order to calculate the - * up-link and down-link frequencies for any numeric channel references - * contained within the message. - */ - if( message.isValid() ) - { - /* Insert band identifier update messages into channel-type messages */ - if( message instanceof IdentifierReceiver ) - { - IdentifierReceiver receiver = (IdentifierReceiver)message; - - int[] identifiers = receiver.getIdentifiers(); - - for( int identifier: identifiers ) - { - receiver.setIdentifierMessage( identifier, - mBandIdentifierMap.get( identifier ) ); - } - } - - /* Store band identifiers so that they can be injected into channel - * type messages */ - if( message instanceof IBandIdentifier ) - { - IBandIdentifier bandIdentifier = (IBandIdentifier)message; - - mBandIdentifierMap.put( bandIdentifier.getIdentifier(), - bandIdentifier ); - } - } - - /** - * Broadcast all valid messages and any LDU voice messages regardless if - * they are valid or not, so that we don't miss any voice frames - */ - if( mMessageListener != null && message.isValid() || message instanceof LDUMessage) - { - mMessageListener.receive( message ); - } } - - public void dispose() - { - mBandIdentifierMap.clear(); - mMessageListener = null; - } - - public void setMessageListener( Listener listener ) + + @Override + public void receive(Message message) { - mMessageListener = listener; + /** + * Capture frequency band identifier messages and inject them into any + * messages that require band information in order to calculate the + * up-link and down-link frequencies for any numeric channel references + * contained within the message. + */ + if(message.isValid()) + { + /* Insert frequency band identifier update messages into channel-type messages */ + if(message instanceof IFrequencyBandReceiver) + { + IFrequencyBandReceiver receiver = (IFrequencyBandReceiver)message; + + List channels = receiver.getChannels(); + + for(IChannelDescriptor channel : channels) + { + int[] frequencyBandIdentifiers = channel.getFrequencyBandIdentifiers(); + + for(int id : frequencyBandIdentifiers) + { + if(mFrequencyBandMap.containsKey(id)) + { + channel.setFrequencyBand(mFrequencyBandMap.get(id)); + } + } + } + } + + /* Store band identifiers so that they can be injected into channel + * type messages */ + if(message instanceof IFrequencyBand) + { + IFrequencyBand bandIdentifier = (IFrequencyBand)message; + mFrequencyBandMap.put(bandIdentifier.getIdentifier(), bandIdentifier); + } + } + + if(mMessageListener != null) + { + mMessageListener.receive(message); + } + } + + public void dispose() + { + mFrequencyBandMap.clear(); + mMessageListener = null; + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25Metadata.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25Metadata.java new file mode 100644 index 00000000..09da35e9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25Metadata.java @@ -0,0 +1,37 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.sample.Listener; + +/** + * Decoded metadata for an APCO25 channel + */ +public class P25Metadata implements Listener +{ + + @Override + public void receive(P25Message p25Message) + { + + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25SyncDetector.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25SyncDetector.java new file mode 100644 index 00000000..e4da11e1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25SyncDetector.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.bits.MultiSyncPatternMatcher; +import io.github.dsheirer.bits.SoftSyncDetector; +import io.github.dsheirer.bits.SyncDetector; +import io.github.dsheirer.dsp.psk.pll.IPhaseLockedLoop; +import io.github.dsheirer.dsp.symbol.Dibit; +import io.github.dsheirer.dsp.symbol.FrameSync; +import io.github.dsheirer.dsp.symbol.ISyncDetectListener; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.sample.Listener; + +public class P25SyncDetector implements Listener +{ + /* Determines the threshold for sync pattern soft matching */ + private static final int SYNC_MATCH_THRESHOLD = 4; + + /* Costas Loop phase lock error correction values. A phase lock error of + * 90 degrees requires a correction of 1/4 of the symbol rate (1200Hz). An + * error of 180 degrees requires a correction of 1/2 of the symbol rate */ + public static final double DEFAULT_SAMPLE_RATE = 50000.0; + + public static final double FREQUENCY_PHASE_CORRECTION_90_DEGREES = 1200.0; + public static final double FREQUENCY_PHASE_CORRECTION_180_DEGREES = 2400.0; + + private MultiSyncPatternMatcher mMatcher; + private SoftSyncDetector mPrimarySyncDetector; + + private PLLPhaseInversionDetector mInversionDetector90CW; + private PLLPhaseInversionDetector mInversionDetector90CCW; + private PLLPhaseInversionDetector mInversionDetector180; + + public P25SyncDetector(ISyncDetectListener syncDetectListener, IPhaseLockedLoop phaseLockedLoop) + { + //TODO: since we're only going to feed dibits to find next frame, it makes sense to + //TODO: update the sync lost parameter to 48 bits .... + + //TODO: only enable the phase inversion detectors when we're in a sync-lost state + mMatcher = new MultiSyncPatternMatcher(syncDetectListener, DataUnitID.LOGICAL_LINK_DATA_UNIT_1.getMessageLength(), 48); + mPrimarySyncDetector = new SoftSyncDetector(FrameSync.P25_PHASE1_NORMAL.getSync(), SYNC_MATCH_THRESHOLD, syncDetectListener); + mMatcher.add(mPrimarySyncDetector); + + if(phaseLockedLoop != null) + { + //Add additional sync pattern detectors to detect when we get 90/180 degree out of phase sync pattern + //detections so that we can apply correction to the phase locked loop + mInversionDetector90CW = new PLLPhaseInversionDetector(FrameSync.P25_PHASE1_ERROR_90_CW, + phaseLockedLoop, DEFAULT_SAMPLE_RATE, FREQUENCY_PHASE_CORRECTION_90_DEGREES); + mMatcher.add(mInversionDetector90CW); + + mInversionDetector90CCW = new PLLPhaseInversionDetector(FrameSync.P25_PHASE1_ERROR_90_CCW, + phaseLockedLoop, DEFAULT_SAMPLE_RATE, -FREQUENCY_PHASE_CORRECTION_90_DEGREES); + mMatcher.add(mInversionDetector90CCW); + + mInversionDetector180 = new PLLPhaseInversionDetector(FrameSync.P25_PHASE1_ERROR_180, + phaseLockedLoop, DEFAULT_SAMPLE_RATE, FREQUENCY_PHASE_CORRECTION_180_DEGREES); + mMatcher.add(mInversionDetector180); + } + } + + /** + * Calculates the number of bits that match in the current primary detector + * @return + */ + public int getPrimarySyncMatchErrorCount() + { + return Long.bitCount(mMatcher.getCurrentValue() ^ FrameSync.P25_PHASE1_NORMAL.getSync()); + } + + @Override + public void receive(Dibit dibit) + { + mMatcher.receive(dibit.getBit1(), dibit.getBit2()); + } + + /** + * Updates the incoming sample stream sample rate to allow the PLL phase inversion detectors to + * recalculate their internal phase correction values. + * + * @param sampleRate of the incoming sample stream + */ + public void setSampleRate(double sampleRate) + { + mInversionDetector180.setSampleRate(sampleRate); + mInversionDetector90CW.setSampleRate(sampleRate); + mInversionDetector90CCW.setSampleRate(sampleRate); + } + + /** + * Sync pattern detector to listen for costas loop phase lock errors and apply a phase correction to the costas + * loop so that we don't miss any messages. + * + * When the costas loop locks with a +/- 90 degree or 180 degree phase error, the slicer will incorrectly apply + * the symbol pattern rotated left or right by the phase error. However, we can detect these rotated sync patterns + * and apply immediate phase correction so that message processing can continue. + */ + public class PLLPhaseInversionDetector extends SyncDetector + { + private IPhaseLockedLoop mPhaseLockedLoop; + private double mSampleRate; + private double mFrequencyCorrection; + private double mPllCorrection; + + /** + * Constructs the PLL phase inversion detector. + * + * @param frameSync pattern to monitor for detecting phase inversion errors + * @param phaseLockedLoop to receive phase correction values + * @param sampleRate of the incoming sample stream + * @param frequencyCorrection to apply to the PLL. Examples: + * QPSK +/-90 degree correction: +/-SYMBOL RATE / 4.0 + * QPSK 180 degree correction: SYMBOL RATE / 2.0 + */ + public PLLPhaseInversionDetector(FrameSync frameSync, IPhaseLockedLoop phaseLockedLoop, double sampleRate, + double frequencyCorrection) + { + super(frameSync.getSync()); + mPhaseLockedLoop = phaseLockedLoop; + mFrequencyCorrection = frequencyCorrection; + setSampleRate(sampleRate); + + setListener(new ISyncDetectListener() + { + @Override + public void syncDetected(int bitErrors) + { + mPhaseLockedLoop.correctInversion(mPllCorrection); + } + + @Override + public void syncLost() + { + //no-op + } + }); + } + + /** + * Sets or adjusts the sample rate so that the phase inversion correction value can be recalculated. + * @param sampleRate + */ + public void setSampleRate(double sampleRate) + { + mSampleRate = sampleRate; + mPllCorrection = 2.0 * Math.PI * mFrequencyCorrection / mSampleRate; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java new file mode 100644 index 00000000..a7bdbb6e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java @@ -0,0 +1,514 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25; + +import io.github.dsheirer.controller.channel.Channel; +import io.github.dsheirer.controller.channel.Channel.ChannelType; +import io.github.dsheirer.controller.channel.ChannelEvent; +import io.github.dsheirer.controller.channel.ChannelEvent.Event; +import io.github.dsheirer.controller.channel.ChannelGrantEvent; +import io.github.dsheirer.controller.channel.IChannelEventListener; +import io.github.dsheirer.controller.channel.IChannelEventProvider; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.module.Module; +import io.github.dsheirer.module.decode.config.DecodeConfiguration; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.DecodeEventType; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEventProvider; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.ServiceOptions; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.source.config.SourceConfigTuner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * Monitors channel grant and channel grant update messages to allocate traffic channels to capture + * traffic channel activity. + * + * Creates and reuses a limited set of Channel instances each with a TRAFFIC channel type. Since each of + * the traffic channels will be using the same decoder type and configuration options, we reuse each of the + * channel instances to allow the ChannelProcessingManager to reuse a cached set of processing chains that + * are created as each channel is activated. + * + * Each traffic channel is activated by sending a ChannelEvent via the channel model. The channel processing + * manager receives the activation request. If successful, a processing chain is activated for the traffic + * channel. Otherwise, a channel event is broadcast indicating that the channel could not be activated. On + * teardown of an activated traffic channel, a channel event is broadcast to indicate the traffic channels + * is no longer active. + * + * This manager monitors channel events watching for events related to managed traffic channels. + */ +public class P25TrafficChannelManager extends Module implements IDecodeEventProvider, IChannelEventListener, + IChannelEventProvider +{ + private final static Logger mLog = LoggerFactory.getLogger(P25TrafficChannelManager.class); + public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED"; + public static final String MAX_TRAFFIC_CHANNELS_EXCEEDED = "MAX TRAFFIC CHANNELS EXCEEDED"; + + private Queue mAvailableTrafficChannelQueue = new ConcurrentLinkedQueue<>(); + private List mManagedTrafficChannels; + private Map mAllocatedTrafficChannelMap = new ConcurrentHashMap<>(); + private Map mChannelGrantEventMap = new ConcurrentHashMap<>(); + + private Listener mChannelEventListener; + private Listener mDecodeEventListener; + + private TrafficChannelTeardownMonitor mTrafficChannelTeardownMonitor = new TrafficChannelTeardownMonitor(); + + private boolean mIgnoreDataCalls = false; + + /** + * Monitors call events and allocates traffic decoder channels in response + * to traffic channel allocation call events. Manages a pool of reusable + * traffic channel allocations. + * + * @param parentChannel that owns this traffic channel manager + */ + public P25TrafficChannelManager(Channel parentChannel) + { + createTrafficChannels(parentChannel); + DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)parentChannel.getDecodeConfiguration(); + mIgnoreDataCalls = p25Config.getIgnoreDataCalls(); + } + + /** + * Creates up to the maximum number of traffic channels for use in allocating traffic channels. + * + * @param parentChannel for inheriting naming, alias list and traffic channel pool size properties + */ + private void createTrafficChannels(Channel parentChannel) + { + DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration(); + List trafficChannelList = new ArrayList<>(); + + if(decodeConfiguration instanceof DecodeConfigP25Phase1) + { + DecodeConfigP25Phase1 p25DecodeConfig = (DecodeConfigP25Phase1)decodeConfiguration; + + int maxTrafficChannels = p25DecodeConfig.getTrafficChannelPoolSize(); + + for(int x = 0; x < maxTrafficChannels; x++) + { + Channel trafficChannel = new Channel("TRAFFIC", ChannelType.TRAFFIC); + trafficChannel.setAliasListName(parentChannel.getAliasListName()); + trafficChannel.setSystem(parentChannel.getSystem()); + trafficChannel.setSite(parentChannel.getSite()); + trafficChannel.setDecodeConfiguration(p25DecodeConfig); + trafficChannel.setEventLogConfiguration(parentChannel.getEventLogConfiguration()); + trafficChannel.setRecordConfiguration(parentChannel.getRecordConfiguration()); + trafficChannelList.add(trafficChannel); + } + } + + mAvailableTrafficChannelQueue.addAll(trafficChannelList); + mManagedTrafficChannels = Collections.unmodifiableList(trafficChannelList); + } + + /** + * Broadcasts an initial or update decode event to any registered listener. + */ + public void broadcast(DecodeEvent decodeEvent) + { + if(mDecodeEventListener != null) + { + mDecodeEventListener.receive(decodeEvent); + } + } + + /** + * Processes channel grants to allocate traffic channels and track overall channel usage. Generates + * decode events for each new channel that is allocated. + * + * @param apco25Channel for the traffic channel + * @param serviceOptions for the traffic channel - optional can be null + * @param identifierCollection associated with the channel grant + * @param opcode to identify the call type for the event description + */ + public void processChannelGrant(APCO25Channel apco25Channel, ServiceOptions serviceOptions, + IdentifierCollection identifierCollection, Opcode opcode, long timestamp) + { + P25ChannelGrantEvent event = mChannelGrantEventMap.get(apco25Channel); + + if(event != null && isSameCall(identifierCollection, event.getIdentifierCollection())) + { + Identifier from = getIdentifier(identifierCollection, Role.FROM); + + if(from != null) + { + Identifier currentFrom = getIdentifier(event.getIdentifierCollection(), Role.FROM); + if(currentFrom != null && !Objects.equals(from, currentFrom)) + { + event.end(timestamp); + + P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + .channel(apco25Channel) + .eventDescription(getEventType(opcode, serviceOptions).toString() + " - Continue") + .details("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .identifiers(identifierCollection) + .build(); + + mChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + broadcast(continuationGrantEvent); + } + } + + //update the ending timestamp so that the duration value is correctly calculated + event.update(timestamp); + broadcast(event); + + //Even though we have an event, the initial channel grant may have been rejected. Check to see if there + //is a traffic channel allocated. If not, allocate one and update the event description. + if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel) && + !(mIgnoreDataCalls && opcode == Opcode.OSP_SNDCP_DATA_CHANNEL_GRANT)) + { + Channel trafficChannel = mAvailableTrafficChannelQueue.poll(); + + if(trafficChannel != null) + { + event.setEventDescription(getEventType(opcode, serviceOptions).toString()); + event.setDetails("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")); + event.setChannelDescriptor(apco25Channel); + broadcast(event); + SourceConfigTuner sourceConfig = new SourceConfigTuner(); + sourceConfig.setFrequency(apco25Channel.getDownlinkFrequency()); + trafficChannel.setSourceConfiguration(sourceConfig); + mAllocatedTrafficChannelMap.put(apco25Channel, trafficChannel); + broadcast(new ChannelGrantEvent(trafficChannel, Event.REQUEST_ENABLE, apco25Channel, identifierCollection)); + } + } + + return; + } + + if(mIgnoreDataCalls && opcode == Opcode.OSP_SNDCP_DATA_CHANNEL_GRANT) + { + P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + .channel(apco25Channel) + .eventDescription(getEventType(opcode, serviceOptions).toString() + " - Ignored") + .details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .identifiers(identifierCollection) + .build(); + + mChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + + broadcast(channelGrantEvent); + return; + } + + P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + .channel(apco25Channel) + .eventDescription(getEventType(opcode, serviceOptions).toString()) + .details("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .identifiers(identifierCollection) + .build(); + + mChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + + //Allocate a traffic channel for the downlink frequency if one isn't already allocated + //NOTE: we could also allocate a traffic channel for the uplink frequency here, in the future + if(!mAllocatedTrafficChannelMap.containsKey(apco25Channel)) + { + Channel trafficChannel = mAvailableTrafficChannelQueue.poll(); + + if(trafficChannel == null) + { + channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED); + channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored"); + return; + } + + SourceConfigTuner sourceConfig = new SourceConfigTuner(); + sourceConfig.setFrequency(apco25Channel.getDownlinkFrequency()); + trafficChannel.setSourceConfiguration(sourceConfig); + mAllocatedTrafficChannelMap.put(apco25Channel, trafficChannel); + broadcast(new ChannelGrantEvent(trafficChannel, Event.REQUEST_ENABLE, apco25Channel, identifierCollection)); + } + + broadcast(channelGrantEvent); + } + + /** + * Creates a call event type description for the specified opcode and service options + */ + private DecodeEventType getEventType(Opcode opcode, ServiceOptions serviceOptions) + { + boolean encrypted = serviceOptions != null ? serviceOptions.isEncrypted() : false; + + DecodeEventType type = null; + + switch(opcode) + { + case OSP_GROUP_VOICE_CHANNEL_GRANT: + case OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE: + case OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + type = encrypted ? DecodeEventType.CALL_GROUP_ENCRYPTED : DecodeEventType.CALL_GROUP; + break; + + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + type = encrypted ? DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED : DecodeEventType.CALL_UNIT_TO_UNIT; + break; + + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + type = encrypted ? DecodeEventType.CALL_INTERCONNECT_ENCRYPTED : DecodeEventType.CALL_INTERCONNECT; + break; + + case OSP_SNDCP_DATA_CHANNEL_GRANT: + case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT: + type = encrypted ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL; + break; + + case MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT: + case MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE: + type = encrypted ? DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED : DecodeEventType.CALL_PATCH_GROUP; + break; + } + + if(type == null) + { + mLog.debug("Unrecognized opcode for determining decode event type: " + opcode.name()); + type = DecodeEventType.CALL; + } + + return type; + } + + /** + * Channel event listener to receive notifications that a traffic channel has ended processing and we + * can reclaim the traffic channel for reuse. + * + * @return listener for processing channel events. + */ + @Override + public Listener getChannelEventListener() + { + return mTrafficChannelTeardownMonitor; + } + + /** + * Broadcasts a channel event to a registered external listener (for action). + */ + private void broadcast(ChannelEvent channelEvent) + { + if(mChannelEventListener != null) + { + mChannelEventListener.receive(channelEvent); + } + } + + /** + * Sets the external channel event listener to receive channel events from this traffic channel manager + */ + @Override + public void setChannelEventListener(Listener listener) + { + mChannelEventListener = listener; + } + + /** + * Removes the channel event listener + */ + @Override + public void removeChannelEventListener() + { + mChannelEventListener = null; + } + + /** + * Compares the TO role identifier(s) from each collection for equality + * + * @param collection1 containing a TO identifier + * @param collection2 containing a TO identifier + * @return true if both collections contain a TO identifier and the TO identifiers are the same value + */ + private boolean isSameCall(IdentifierCollection collection1, IdentifierCollection collection2) + { + Identifier toIdentifier1 = getIdentifier(collection1, Role.TO); + Identifier toIdentifier2 = getIdentifier(collection2, Role.TO); + return Objects.equals(toIdentifier1, toIdentifier2); + } + + /** + * Retrieves the first identifier with a TO role. + * + * @param collection containing a TO identifier + * @return TO identifier or null + */ + private Identifier getIdentifier(IdentifierCollection collection, Role role) + { + List identifiers = collection.getIdentifiers(role); + + if(identifiers.size() >= 1) + { + return identifiers.get(0); + } + + return null; + } + + @Override + public void dispose() + { + for(Channel trafficChannel : mAvailableTrafficChannelQueue) + { + broadcast(new ChannelEvent(trafficChannel, Event.REQUEST_DISABLE)); + } + + mAvailableTrafficChannelQueue.clear(); + +// mTrafficChannelsInUse.clear(); + } + + /** + * Implements the IDecodeEventProvider interface to provide channel events to an external listener. + */ + @Override + public void addDecodeEventListener(Listener listener) + { + mDecodeEventListener = listener; + } + + /** + * Removes the external decode event listener + */ + @Override + public void removeDecodeEventListener(Listener listener) + { + mDecodeEventListener = null; + } + + @Override + public void reset() + { + } + + @Override + public void start() + { + } + + @Override + public void stop() + { + List channels = new ArrayList<>(mAllocatedTrafficChannelMap.values()); + + //Issue a disable request for each traffic channel + for(Channel channel: channels) + { + mLog.debug("Stopping traffic channel: " + channel); + broadcast(new ChannelEvent(channel, Event.REQUEST_DISABLE)); + } + } + + /** + * Monitors channel teardown events to detect when traffic channel processing has ended. Reclaims the + * channel instance for reuse by future traffic channel grants. + */ + public class TrafficChannelTeardownMonitor implements Listener + { + @Override + public synchronized void receive(ChannelEvent channelEvent) + { + Channel channel = channelEvent.getChannel(); + + if(channel.isTrafficChannel() && mManagedTrafficChannels.contains(channel)) + { + switch(channelEvent.getEvent()) + { + case NOTIFICATION_PROCESSING_STOP: + APCO25Channel toRemove = null; + + for(Map.Entry entry: mAllocatedTrafficChannelMap.entrySet()) + { + if(entry.getValue() == channel) + { + toRemove = entry.getKey(); + continue; + } + } + + if(toRemove != null) + { + mAllocatedTrafficChannelMap.remove(toRemove); + mAvailableTrafficChannelQueue.add(channel); + P25ChannelGrantEvent event = mChannelGrantEventMap.remove(toRemove); + + if(event != null) + { + event.end(System.currentTimeMillis()); + broadcast(event); + } + } + break; + case NOTIFICATION_PROCESSING_START_REJECTED: + APCO25Channel rejected = null; + + for(Map.Entry entry: mAllocatedTrafficChannelMap.entrySet()) + { + if(entry.getValue() == channel) + { + rejected = entry.getKey(); + continue; + } + } + + if(rejected != null) + { + mAllocatedTrafficChannelMap.remove(rejected); + mAvailableTrafficChannelQueue.add(channel); + P25ChannelGrantEvent event = mChannelGrantEventMap.get(rejected); + + if(event != null) + { + event.setEventDescription(event.getEventDescription() + " - Rejected"); + + if(channelEvent.getDescription() != null) + { + event.setDetails(channelEvent.getDescription() + " - " + event.getDetails()); + } + else + { + event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails()); + } + + broadcast(event); + } + } + break; + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25Utils.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25Utils.java new file mode 100644 index 00000000..62c0c296 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25Utils.java @@ -0,0 +1,62 @@ +package io.github.dsheirer.module.decode.p25; + +public class P25Utils +{ + /** + * Formats a NAC code to three hexadecimal characters using zeros to prepad the value to three places. + * @param nac to format + * @return formatted nac + */ + public static String formatNAC(int nac) + { + return formatHex(nac, 3); + } + + /** + * Formats the value as hexadecimal with the minimum number of character places prepadding with zeros as needed. + * @param value to format + * @param places minimum for the formatted value + * @return formatted value + */ + public static String formatHex(int value, int places) + { + StringBuilder sb = new StringBuilder(); + String formatted = Integer.toHexString(value).toUpperCase(); + + if(formatted.length() < places) + { + for(int x = 0; x < (places - formatted.length()); x++) + { + sb.append("0"); + } + } + + sb.append(formatted); + + return sb.toString(); + } + + /** + * Adds spaces to the string builder until it is the specified length + * @param sb to pad with spaces + * @param length of the stringbuilder when complete + */ + public static void pad(StringBuilder sb, int length) + { + pad(sb, length, " "); + } + + /** + * Adds pad characters to the string builder until it is the specified length + * @param sb to pad with spaces + * @param length of the stringbuilder when complete + * @param padCharacter to use for padding + */ + public static void pad(StringBuilder sb, int length, String padCharacter) + { + while(sb.length() < length) + { + sb.append(padCharacter); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/PatchGroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/PatchGroup.java deleted file mode 100644 index 97a1e923..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/PatchGroup.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25; - -import java.util.ArrayList; -import java.util.List; - -public class PatchGroup -{ - private long mExpireThreshold = 5000; //5 seconds - private long mLastUpdated; - private String mPatchGroupID; - private List mPatchedGroups = new ArrayList<>(); - - /** - * Constructs a patch group with the specified patch group ID - * @param patchGroupID that identifies this patch group - */ - public PatchGroup(String patchGroupID) - { - mPatchGroupID = patchGroupID; - mLastUpdated = System.currentTimeMillis(); - } - - /** - * Patch group ID for this patch group - */ - public String getPatchGroupID() - { - return mPatchGroupID; - } - - /** - * Adds the patched group to this patch group. Duplicate patched groups are ignored. - * - * Note: the patch group ID will not be added as a patched group, to avoid circular dependencies. - * - * @return true if the patched group was added to this patch group - */ - public boolean addPatchedGroup(String patchedGroup) - { - if(patchedGroup != null && !patchedGroup.equals(mPatchGroupID) && !mPatchedGroups.contains(patchedGroup)) - { - mPatchedGroups.add(patchedGroup); - return true; - } - - return false; - } - - /** - * Patched talkgroups that are part of this patch group. - */ - public List getPatchedGroups() - { - return mPatchedGroups; - } - - /** - * Updates the timestamp for this patch group. - */ - public void updateTimestamp() - { - mLastUpdated = System.currentTimeMillis(); - } - - /** - * Indicates if this patch group is expired, meaning that it hasn't been updated in the past (expire - * threshold) seconds. - */ - public boolean isExpired() - { - return System.currentTimeMillis() - mLastUpdated > mExpireThreshold; - } - - /** - * Sets the patch group expiration threshold (default = 5000 ms). A patch group is considered expired if - * no new updates have been received within the past (expire threshold) milliseconds - * - * @param expireThreshold in milliseconds - */ - public void setExpireThreshold(long expireThreshold) - { - mExpireThreshold = expireThreshold; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/PatchGroupManager.java b/src/main/java/io/github/dsheirer/module/decode/p25/PatchGroupManager.java deleted file mode 100644 index 1208ab22..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/PatchGroupManager.java +++ /dev/null @@ -1,245 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.PatchGroupAlias; -import io.github.dsheirer.alias.id.talkgroup.TalkgroupID; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.sample.Broadcaster; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class PatchGroupManager -{ - private AliasList mAliasList; - private List mPatchGroups = new ArrayList<>(); - private Broadcaster mCallEventBroadcaster; - - /** - * Manages active P25 patch groups and auto-expires patch groups if not updated every 5 seconds. - * - * @param aliasList to keep updated with patch group changes - * @param broadcaster for rebroadcasting patch group related call events - */ - public PatchGroupManager(AliasList aliasList, Broadcaster broadcaster) - { - mAliasList = aliasList; - mCallEventBroadcaster = broadcaster; - } - - /** - * Removes the patch group. - * - * @param patchGroupID of the patch group to remove - */ - public void removePatchGroup(String patchGroupID) - { - removePatchGroup(patchGroupID, false); - } - - /** - * Removes the patch group. - * - * @param patchGroupID of the patch group to remove - * @param expired true if the patch group is being removed because it expired, false if we receive a - * message commanding commanding a patch group delete. - */ - private void removePatchGroup(String patchGroupID, boolean expired) - { - PatchGroup patchGroup = getPatchGroup(patchGroupID); - - if(patchGroup != null) - { - //Cleanup the alias list - if(mAliasList != null) - { - Alias alias = mAliasList.getTalkgroupAlias(patchGroupID); - - if(alias instanceof PatchGroupAlias) - { - PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias; - - mAliasList.removeAlias(patchGroupAlias); - - //Replace our temporary patch group alias with the original alias for the patch group ID - if(patchGroupAlias.hasPatchGroupAlias()) - { - mAliasList.addAlias(patchGroupAlias.getPatchGroupAlias()); - } - } - } - - mPatchGroups.remove(patchGroup); - - if(mCallEventBroadcaster != null) - { - StringBuilder sb = new StringBuilder(); - - sb.append((expired ? "EXPIRED " : "DELETED ")); - sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID()); - sb.append(" ").append(patchGroup.getPatchedGroups()); - - - mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_DELETE) - .to(patchGroup.getPatchGroupID()) - .details(sb.toString()) - .build()); - } - } - } - - /** - * Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the - * patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and - * replaced with the aliases corresponding to the patched talkgroup aliases. - * - * @param patchGroupID for the patch group - * @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups - */ - public void updatePatchGroup(String patchGroupID, List patchedTalkgroups) - { - boolean updated = false; - - PatchGroup patchGroup = getPatchGroup(patchGroupID); - - if(patchGroup == null) - { - //Remove any expired patch groups before we add a new one - cleanupPatchGroups(); - patchGroup = new PatchGroup(patchGroupID); - mPatchGroups.add(patchGroup); - updated = true; - } - else - { - patchGroup.updateTimestamp(); - } - - for(String patchedGroup : patchedTalkgroups) - { - if(patchGroup.addPatchedGroup(patchedGroup)) - { - updated = true; - } - } - - //If we make any changes to the patch group, update the patch group alias - if(updated) - { - PatchGroupAlias patchGroupAlias = null; - - //Check for an existing alias for the patch talkgroup - do not include wildcard aliases - if(mAliasList != null) - { - Alias existingAlias = mAliasList.getTalkgroupAlias(patchGroupID, false); - - if(existingAlias instanceof PatchGroupAlias) - { - patchGroupAlias = (PatchGroupAlias)existingAlias; - } - else - { - patchGroupAlias = new PatchGroupAlias(); - - patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID)); - - if(existingAlias != null) - { - mAliasList.removeAlias(existingAlias); - - patchGroupAlias.setPatchGroupAlias(existingAlias); - } - - mAliasList.addAlias(patchGroupAlias); - } - - if(patchGroupAlias != null) - { - patchGroupAlias.setPatchedTalkgroupIDs(patchGroup.getPatchedGroups()); - - patchGroupAlias.clearPatchedAliases(); - - for(String patchedTalkgroup : patchGroup.getPatchedGroups()) - { - Alias patchedAlias = mAliasList.getTalkgroupAlias(patchedTalkgroup); - - if(patchedAlias != null) - { - patchGroupAlias.addPatchedAlias(patchedAlias); - } - } - } - } - - if(mCallEventBroadcaster != null) - { - StringBuilder sb = new StringBuilder(); - - sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID()); - sb.append(" ").append(patchGroup.getPatchedGroups()); - - mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_ADD) - .aliasList(mAliasList) - .details(sb.toString()) - .build()); - } - } - } - - /** - * Returns the patch group matching the patch group ID or null - */ - private PatchGroup getPatchGroup(String patchGroupID) - { - for(PatchGroup patchGroup : mPatchGroups) - { - if(patchGroup.getPatchGroupID().equals(patchGroupID)) - { - return patchGroup; - } - } - - return null; - } - - /** - * Checks the last updated timestamp on all patch groups and removes any patch groups that have not been - * updated in the past 5 seconds - */ - public void cleanupPatchGroups() - { - Iterator it = mPatchGroups.iterator(); - - while(it.hasNext()) - { - PatchGroup patchGroup = it.next(); - - if(patchGroup.isExpired()) - { - it.remove(); - - removePatchGroup(patchGroup.getPatchGroupID(), true); - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/Trellis_1_2_Rate.java b/src/main/java/io/github/dsheirer/module/decode/p25/Trellis_1_2_Rate.java deleted file mode 100644 index 25c8b19a..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/Trellis_1_2_Rate.java +++ /dev/null @@ -1,533 +0,0 @@ -package io.github.dsheirer.module.decode.p25; - -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.dsp.symbol.Dibit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Random; - -public class Trellis_1_2_Rate -{ - public final static int MAX_ERROR_THRESHOLD = 7; - - private final static Logger mLog = - LoggerFactory.getLogger( Trellis_1_2_Rate.class ); - - private ArrayList mConstellationNodes = - new ArrayList(); - - private static final int[][] CONSTELLATION_COSTS = - { { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }, - { 1,0,2,1,2,1,3,2,2,1,3,2,3,2,4,3 }, - { 1,2,0,1,2,3,1,2,2,3,1,2,3,4,2,3 }, - { 2,1,1,0,3,2,2,1,3,2,2,1,4,3,3,2 }, - { 1,2,2,3,0,1,1,2,2,3,3,4,1,2,2,3 }, - { 2,1,3,2,1,0,2,1,3,2,4,3,2,1,3,2 }, - { 2,3,1,2,1,2,0,1,3,4,2,3,2,3,1,2 }, - { 3,2,2,1,2,1,1,0,4,3,3,2,3,2,2,1 }, - { 1,2,2,3,2,3,3,4,0,1,1,2,1,2,2,3 }, - { 2,1,3,2,3,2,4,3,1,0,2,1,2,1,3,2 }, - { 2,3,1,2,3,4,2,3,1,2,0,1,2,3,1,2 }, - { 3,2,2,1,4,3,3,2,2,1,1,0,3,2,2,1 }, - { 2,3,3,4,1,2,2,3,1,2,2,3,0,1,1,2 }, - { 3,2,4,3,2,1,3,2,2,1,3,2,1,0,2,1 }, - { 3,4,2,3,2,3,1,2,2,3,1,2,1,2,0,1 }, - { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 } }; - - public Trellis_1_2_Rate() - { - /* Create 49 x 4-bit constellation nodes to handle 196-bit blocks */ - ConstellationNode previous = null; - - for( int x = 0; x < 49; x++ ) - { - ConstellationNode node = new ConstellationNode(); - - if( previous != null ) - { - previous.connect( node ); - } - - previous = node; - - mConstellationNodes.add( node ); - } - } - - public void dispose() - { - for( ConstellationNode node: mConstellationNodes ) - { - node.dispose(); - } - - mConstellationNodes.clear(); - } - -// private String constellationsToString( String label ) -// { -// StringBuilder sb = new StringBuilder(); -// -// sb.append( label ); -// -// for( ConstellationNode node: mConstellationNodes ) -// { -// sb.append( " " + node.getConstellation().name() ); -// } -// -// return sb.toString(); -// } -// -// private String inputsToString() -// { -// StringBuilder sb = new StringBuilder(); -// -// sb.append( " INPUTS: " ); -// -// for( ConstellationNode node: mConstellationNodes ) -// { -// sb.append( " " ); -// sb.append( ( node.getConstellation().getInput().getBit1() ? "1" : "0" ) ); -// sb.append( ( node.getConstellation().getInput().getBit2() ? "1" : "0" ) ); -// } -// -// return sb.toString(); -// } - - public boolean decode( BinaryMessage message, int start, int end ) - { - /* load each of the nodes with de-interleaved constellations */ - for( int index = 0; index < 49; index++ ) - { - Constellation c = getConstellation( message, start + index * 4 ); - - mConstellationNodes.get( index ).setConstellation( c ); - } - - /* test to see if constellations are correct - otherwise correct them */ - ConstellationNode firstNode = mConstellationNodes.get( 0 ); - - int errorCount = firstNode.getErrorCount(); - - if( errorCount > 0 ) - { - if( errorCount < MAX_ERROR_THRESHOLD ) - { - /* repair the errors */ - firstNode.correctTo( Dibit.D00_PLUS_1 ); - } - else - { - return false; - } - } - - /* clear constellations from original message */ - message.clear( start, end ); - - /* replace with decoded values from the constellation nodes */ - for( int index = 0; index < 49; index++ ) - { - ConstellationNode node = mConstellationNodes.get( index ); - - Dibit input = node.getInputDibit(); - - if( input.getBit1() ) - { - message.set( start + ( index * 2 ) ); - } - if( input.getBit2() ) - { - message.set( start + ( index * 2 ) + 1 ); - } - } - - return true; - } - - private Constellation getConstellation( BinaryMessage message, int index ) - { - int transmittedValue = 0; - - for( int x = 0; x < 4; x++ ) - { - if( message.get( index + x ) ) - { - transmittedValue += ( 1 << ( 3 - x ) ); - } - } - - return Constellation.fromTransmittedValue( transmittedValue ); - } - - public class ConstellationNode - { - private ConstellationNode mConnectedNode; - private Constellation mConstellation; - private boolean mCorrect; - - public ConstellationNode() - { - } - - public void dispose() - { - mConnectedNode = null; - } - - public Constellation getConstellation() - { - return mConstellation; - } - - public Dibit getInputDibit() - { - return mConstellation.getInput(); - } - - public boolean hasStateDibit( Dibit dibit ) - { - return mConstellation.getState() == dibit; - } - - /** - * Executes a correction down the line of connected nodes. Only nodes - * with the mCorrect flag set to false will be corrected. - * - * Note: Assumes that the starting node's value is 0. Initiate the - * corrective sequence by invoking this method with Dibit.D0 on the - * first node. - * - * @param stateDibit to use for the left side. - */ - public void correctTo( Dibit stateDibit ) - { - if( mCorrect && mConstellation.getState() == stateDibit ) - { - return; - } - - if( isCurrentConnectionCorrect() ) - { - mConstellation = Constellation.fromStateAndInputDibits( stateDibit, - mConstellation.getInput() ); - - mCorrect = true; - - if( mConnectedNode != null ) - { - /* the next node's state was this node's input */ - mConnectedNode.correctTo( mConstellation.getInput() ); - } - } - else - { - Constellation cheapestConstellation = mConstellation; - - int cheapestCost = 100; //arbitrary - - for( Dibit testInput: Dibit.values() ) - { - Constellation testConstellation = Constellation - .fromStateAndInputDibits( stateDibit, testInput ); - - int testCost = mConstellation.costTo( testConstellation ) + - mConnectedNode.costTo( testInput ); - - if( testCost < cheapestCost ) - { - cheapestCost = testCost; - cheapestConstellation = testConstellation; - } - } - - mConstellation = cheapestConstellation; - - mConnectedNode.correctTo( mConstellation.getInput() ); - - mCorrect = true; - } - } - - /** - * Calculates the cost (hamming distance) of using the argument as the - * state dibit for the current node, and recursively finding the - * cheapest corresponding input dibit. - * - * @param stateTest - * @return - */ - public int costTo( Dibit stateTest ) - { - if( isCurrentConnectionCorrect() ) - { - Constellation c = Constellation.fromStateAndInputDibits( - stateTest, mConstellation.getInput() ); - - return mConstellation.costTo( c ); - } - else - { - int cheapestCost = 100; //arbitrary - - for( Dibit inputTest: Dibit.values() ) - { - Constellation constellationTest = - Constellation.fromStateAndInputDibits( stateTest, inputTest ); - - int cost = mConnectedNode.costTo( inputTest ) + - mConstellation.costTo( constellationTest ); - - if( cost < cheapestCost ) - { - cheapestCost = cost; - } - } - - return cheapestCost; - } - } - - /** - * Indicates if the immediate connection to the right is correct - */ - public boolean isCurrentConnectionCorrect() - { - return ( mConnectedNode == null || - mConstellation.getInput() == mConnectedNode.getStateDibit() ); - } - - /** - * Executes a recursive call to all nodes to the right, setting the - * mCorrect flag on all nodes to true, if the node's connection to the - * right node is correct and all nodes to the right are correct. Or, - * false if this node's connection, or any node's connection to the - * right is incorrect. - * - * @return - true - all node connections to the right are correct - * false - one or more nodes to the right are incorrect - */ - public int getErrorCount() - { - if( mConnectedNode == null ) - { - mCorrect = true; - - return 0; - } - - mCorrect = mConstellation.getInput() == mConnectedNode.getStateDibit(); - - return mConnectedNode.getErrorCount() + ( mCorrect ? 0 : 1 ); - } - - public Dibit getStateDibit() - { - return mConstellation.getState(); - } - - public void setConstellation( Constellation constellation ) - { - mConstellation = constellation; - mCorrect = false; - } - - public void connect( ConstellationNode node ) - { - mConnectedNode = node; - } - } - - public enum Constellation - { - /* State (previous), Input (next), Transmit 1, Transmit 2 */ - CB( Dibit.D01_PLUS_3, Dibit.D01_PLUS_3, Dibit.D00_PLUS_1, Dibit.D00_PLUS_1, 0 ), - CC( Dibit.D00_PLUS_1, Dibit.D10_MINUS_1, Dibit.D00_PLUS_1, Dibit.D01_PLUS_3, 1 ), - C0( Dibit.D00_PLUS_1, Dibit.D00_PLUS_1, Dibit.D00_PLUS_1, Dibit.D10_MINUS_1, 2 ), - C7( Dibit.D01_PLUS_3, Dibit.D11_MINUS_3, Dibit.D00_PLUS_1, Dibit.D11_MINUS_3, 3 ), - CE( Dibit.D10_MINUS_1, Dibit.D11_MINUS_3, Dibit.D01_PLUS_3, Dibit.D00_PLUS_1, 4 ), - C9( Dibit.D11_MINUS_3, Dibit.D00_PLUS_1, Dibit.D01_PLUS_3, Dibit.D01_PLUS_3, 5 ), - C5( Dibit.D11_MINUS_3, Dibit.D10_MINUS_1, Dibit.D01_PLUS_3, Dibit.D10_MINUS_1, 6 ), - C2( Dibit.D10_MINUS_1, Dibit.D01_PLUS_3, Dibit.D01_PLUS_3, Dibit.D11_MINUS_3, 7 ), - CA( Dibit.D11_MINUS_3, Dibit.D11_MINUS_3, Dibit.D10_MINUS_1, Dibit.D00_PLUS_1, 8 ), - CD( Dibit.D10_MINUS_1, Dibit.D00_PLUS_1, Dibit.D10_MINUS_1, Dibit.D01_PLUS_3, 9 ), - C1( Dibit.D10_MINUS_1, Dibit.D10_MINUS_1, Dibit.D10_MINUS_1, Dibit.D10_MINUS_1, 10 ), - C6( Dibit.D11_MINUS_3, Dibit.D01_PLUS_3, Dibit.D10_MINUS_1, Dibit.D11_MINUS_3, 11 ), - CF( Dibit.D00_PLUS_1, Dibit.D01_PLUS_3, Dibit.D11_MINUS_3, Dibit.D00_PLUS_1, 12 ), - C8( Dibit.D01_PLUS_3, Dibit.D10_MINUS_1, Dibit.D11_MINUS_3, Dibit.D01_PLUS_3, 13 ), - C4( Dibit.D01_PLUS_3, Dibit.D00_PLUS_1, Dibit.D11_MINUS_3, Dibit.D10_MINUS_1, 14 ), - C3( Dibit.D00_PLUS_1, Dibit.D11_MINUS_3, Dibit.D11_MINUS_3, Dibit.D11_MINUS_3, 15 ); - - private Dibit mStateDibit; - private Dibit mInputDibit; - private Dibit mTransmitDibit1; - private Dibit mTransmitDibit2; - private int mTransmittedValue; - - private Constellation( Dibit stateDibit, - Dibit inputDibit, - Dibit transmitDibit1, - Dibit transmitDibit2, - int transmittedValue ) - { - mStateDibit = stateDibit; - mInputDibit = inputDibit; - mTransmitDibit1 = transmitDibit1; - mTransmitDibit2 = transmitDibit2; - mTransmittedValue = transmittedValue; - } - - public Dibit getState() - { - return mStateDibit; - } - - public Dibit getInput() - { - return mInputDibit; - } - - public Dibit getTransmitDibit1() - { - return mTransmitDibit1; - } - - public Dibit getTransmitDibit2() - { - return mTransmitDibit2; - } - - public int getTransmittedValue() - { - return mTransmittedValue; - } - - public static Constellation fromTransmittedValue( int value ) - { - if( 0 <= value && value <= 15 ) - { - return values()[ value ]; - } - - return null; - } - - public static Constellation fromTransmittedDibits( Dibit left, Dibit right ) - { - return fromTransmittedValue( left.getHighValue() + right.getLowValue() ); - } - - public static Constellation fromStateAndInputDibits( Dibit state, Dibit input ) - { - switch( state ) - { - case D00_PLUS_1: - switch( input ) - { - case D00_PLUS_1: - return C0; - case D01_PLUS_3: - return CF; - case D10_MINUS_1: - return CC; - case D11_MINUS_3: - return C3; - } - break; - case D01_PLUS_3: - switch( input ) - { - case D00_PLUS_1: - return C4; - case D01_PLUS_3: - return CB; - case D10_MINUS_1: - return C8; - case D11_MINUS_3: - return C7; - } - break; - case D10_MINUS_1: - switch( input ) - { - case D00_PLUS_1: - return CD; - case D01_PLUS_3: - return C2; - case D10_MINUS_1: - return C1; - case D11_MINUS_3: - return CE; - } - break; - case D11_MINUS_3: - switch( input ) - { - case D00_PLUS_1: - return C9; - case D01_PLUS_3: - return C6; - case D10_MINUS_1: - return C5; - case D11_MINUS_3: - return CA; - } - break; - } - - /* Should never get to here */ - return C0; - } - - /** - * Returns the cost or hamming distance to the other constellation using - * the values from the constellation costs table - */ - public int costTo( Constellation other ) - { - return CONSTELLATION_COSTS[ getTransmittedValue() ][ other.getTransmittedValue() ]; - } - - } - - /** - * Test harness - */ - public static void main( String[] args ) - { - Random random = new Random(); - - BinaryMessage buffer = new BinaryMessage( 196 ); - - try - { - for( int x = 0; x < 196; x++ ) - { - int number = random.nextInt( 2 ); - - if( number == 1 ) - { - buffer.add( false ); - } - else - { - buffer.add( true ); - } - } - } - catch( Exception e ) - { - e.printStackTrace(); - } - - mLog.debug( " BUFFER: " + buffer.toString() ); - - Trellis_1_2_Rate t = new Trellis_1_2_Rate(); - - t.decode( buffer, 0, 196 ); - mLog.debug( " DECODED: " + buffer.toString() ); - mLog.debug( "Finished!" ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/Trellis_3_4_Rate.java b/src/main/java/io/github/dsheirer/module/decode/p25/Trellis_3_4_Rate.java deleted file mode 100644 index cc38fa45..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/Trellis_3_4_Rate.java +++ /dev/null @@ -1,661 +0,0 @@ -package io.github.dsheirer.module.decode.p25; - -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.dsp.symbol.Dibit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -public class Trellis_3_4_Rate -{ - private final static Logger mLog = - LoggerFactory.getLogger( Trellis_3_4_Rate.class ); - - /* Hamming distance (bit match count) between constellation pairs */ - private static final int[][] CONSTELLATION_METRICS = - { { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 }, - { 3,4,2,3,2,3,1,2,2,3,1,2,1,2,0,1 }, - { 3,2,4,3,2,1,3,2,2,1,3,2,1,0,2,1 }, - { 2,3,3,4,1,2,2,3,1,2,2,3,0,1,1,2 }, - { 3,2,2,1,4,3,3,2,2,1,1,0,3,2,2,1 }, - { 2,3,1,2,3,4,2,3,1,2,0,1,2,3,1,2 }, - { 2,1,3,2,3,2,4,3,1,0,2,1,2,1,3,2 }, - { 1,2,2,3,2,3,3,4,0,1,1,2,1,2,2,3 }, - { 3,2,2,1,2,1,1,0,4,3,3,2,3,2,2,1 }, - { 2,3,1,2,1,2,0,1,3,4,2,3,2,3,1,2 }, - { 2,1,3,2,1,0,2,1,3,2,4,3,2,1,3,2 }, - { 1,2,2,3,0,1,1,2,2,3,3,4,1,2,2,3 }, - { 2,1,1,0,3,2,2,1,3,2,2,1,4,3,3,2 }, - { 1,2,0,1,2,3,1,2,2,3,1,2,3,4,2,3 }, - { 1,0,2,1,2,1,3,2,2,1,3,2,3,2,4,3 }, - { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 } }; - - /* Even valued state tribits can only produce even valued constellations - * and odd valued state tribits can only produce odd constellations. */ - public static Con[] EVEN_CONSTELLATIONS = new Con[] { Con.C0,Con.C2,Con.C4, - Con.C6,Con.C8,Con.CA,Con.CC,Con.CE }; - public static Con[] ODD_CONSTELLATIONS = new Con[] { Con.C1,Con.C3,Con.C5, - Con.C7,Con.C9,Con.CB,Con.CD,Con.CF }; - - /* Constellation and state tribit lookup map to find the input tribit */ - public static HashMap INPUT_FROM_CONSTELLATION_MAP; - - private ArrayList mTransmittedConstellations = new ArrayList(); - private ArrayList mSurvivorPaths = new ArrayList(); - private ArrayList mNewPaths = new ArrayList(); - private PathMetrics mPathMetrics = new PathMetrics(); - - /** - * Implements the Viterbi algorithm to decode 3/4 rate trellis encoded 196-bit - * packet data messages. - */ - public Trellis_3_4_Rate() - { - createConstellationToTribitMap(); - } - - /** - * Creates a lookup map for state to input tribit values for a given - * constellation. Input tribits are contained in the array using the state - * tribit's value as the lookup index. Null values indicate illegal state - * and input combinations for the specified constellation. - */ - private void createConstellationToTribitMap() - { - INPUT_FROM_CONSTELLATION_MAP = new HashMap(); - - INPUT_FROM_CONSTELLATION_MAP.put( Con.CB, new Tribit[] { null, null, Tribit.T5, Tribit.T3, Tribit.T1, Tribit.T7, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.CC, new Tribit[] { Tribit.T3, Tribit.T1, null, null, null, null, Tribit.T7, Tribit.T5 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C0, new Tribit[] { Tribit.T0, Tribit.T6, null, null, null, null, Tribit.T4, Tribit.T2 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C7, new Tribit[] { null, null, Tribit.T6, Tribit.T4, Tribit.T2, Tribit.T0, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.CE, new Tribit[] { Tribit.T7, Tribit.T5, null, null, null, null, Tribit.T3, Tribit.T1 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C9, new Tribit[] { null, null, Tribit.T1, Tribit.T7, Tribit.T5, Tribit.T3, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C5, new Tribit[] { null, null, Tribit.T2, Tribit.T0, Tribit.T6, Tribit.T4, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C2, new Tribit[] { Tribit.T4, Tribit.T2, null, null, null, null, Tribit.T0, Tribit.T6 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.CA, new Tribit[] { Tribit.T5, Tribit.T3, null, null, null, null, Tribit.T1, Tribit.T7 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.CD, new Tribit[] { null, null, Tribit.T3, Tribit.T1, Tribit.T7, Tribit.T5, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C1, new Tribit[] { null, null, Tribit.T0, Tribit.T6, Tribit.T4, Tribit.T2, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C6, new Tribit[] { Tribit.T6, Tribit.T4, null, null, null, null, Tribit.T2, Tribit.T0 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.CF, new Tribit[] { null, null, Tribit.T7, Tribit.T5, Tribit.T3, Tribit.T1, null, null } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C8, new Tribit[] { Tribit.T1, Tribit.T7, null, null, null, null, Tribit.T5, Tribit.T3 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C4, new Tribit[] { Tribit.T2, Tribit.T0, null, null, null, null, Tribit.T6, Tribit.T4 } ); - INPUT_FROM_CONSTELLATION_MAP.put( Con.C3, new Tribit[] { null, null, Tribit.T4, Tribit.T2, Tribit.T0, Tribit.T6, null, null } );; - } - - /** - * Member object cleanup prior to deleting - */ - public void dispose() - { - reset(); - } - - /** - * Decodes a 196-bit 3/4 rate convolutional encoded message located between - * start and end indexes and returns the decoded 144-bit message overlayed - * upon the original message with the remaining 52 bits cleared to zero. - * - * @return - original message with decoded message bits.. - */ - public boolean decode( BinaryMessage message, int start, int end ) - { - reset(); - - /* Load and decode each of the transmitted constellations */ - for( int index = 0; index < 49; index++ ) - { - Con c = getConstellation( message, start + index * 4 ); - - add( c ); - } - - /* The final decoded survivor path should be at path metrics tribit 0 */ - Path path = mPathMetrics.getPath( Tribit.T0 ); - - /* We should have a path with 50 nodes, counting the starting 000 node - * and the flushing 000 node, otherwise there was an error */ - if( path != null && path.getNodes().size() == 50 ) - { - /* Clear the original message bits */ - message.clear( start, end ); - - List nodes = path.getNodes(); - - /* Load each of the nodes' state tribit values into the original message */ - for( int x = 1; x < 50; x++ ) - { - message.load( start + ( ( x - 1 ) * 3 ), 3, - nodes.get( x ).getState().getValue() ); - } - - return true; - } - - return false; - } - - /** - * Resets the decoder before decoding a new data packet. - */ - private void reset() - { - mTransmittedConstellations.clear(); - mNewPaths.clear(); - - mPathMetrics.reset(); - - mSurvivorPaths.clear(); - - /* Tribit 000 is the only legal start point */ - mSurvivorPaths.add( new Path( new Node( 0, Tribit.T0, Con.C0 ) ) ); - } - - private void add( Con con ) - { - mTransmittedConstellations.add( con ); - - /* Add in any newly created survivor paths */ - mSurvivorPaths.addAll( mNewPaths ); - mNewPaths.clear(); - - /* Set a culling threshold for any path metrics 3 below the best path */ - int survivorThreshold = mPathMetrics.getSurvivorThreshold(); - - /* Reset path metrics */ - mPathMetrics.reset(); - - /* Add constellation to each survivor path. If a survivor path metric - * falls below the culling threshold, remove it */ - Iterator it = mSurvivorPaths.iterator(); - - Path path; - - while( it.hasNext() ) - { - path = it.next(); - - if( path.isDead() || path.getPathMetric() < survivorThreshold ) - { - it.remove(); - } - else - { - path.add( con ); - } - } - } - - private Con getConstellation( BinaryMessage message, int index ) - { - int value = message.getInt( index, index + 3 ); - return Con.fromTransmittedValue( value ); - } - - /** - * Path metrics maintains a map of the current best path metrics that end - * at each of the 8 valid tribit values. Each new path is evaluated against - * the current best path for retention (survivor) or disposal (dead). A - * best metric is maintained to evaluate the Walking Dead for disposal. - * Walking dead is a path lagging the best path metric by more than 3. - */ - public class PathMetrics - { - private HashMap mMetrics = new HashMap(); - - private int mBestMetric = 0; - - public PathMetrics() - { - } - - public int getSurvivorThreshold() - { - return mBestMetric - 3; - } - - /** - * Removes all path metrics - */ - public void reset() - { - mMetrics.clear(); - mBestMetric = 0; - } - - public int getMetric( Tribit tribit ) - { - if( mMetrics.containsKey( tribit ) ) - { - return mMetrics.get( tribit ).getPathMetric(); - } - - return 0; - } - - public Path getPath( Tribit tribit ) - { - return mMetrics.get( tribit ); - } - - /** - * Evaluates the path's metric against the current best path metric for - * the final state tribit of the path. If the path's metric is higher - * than the current best path metric, the current path is marked for - * termination and the new path is promoted to best path. If the new - * path's metric is less than the best path metric, then the new path - * metric is marked for termination. If the metrics are equal, the new - * path is allowed to survive. - * - * @param path/metric to evaluate - */ - public void evalute( Path path ) - { - Node lastNode = path.getLastNode(); - - Path bestPath = mMetrics.get( lastNode.getState() ); - - if( bestPath == null ) - { - mMetrics.put( lastNode.getState(), path ); - } - else - { - if( path.getPathMetric() < bestPath.getPathMetric() ) - { - path.setSurvivor( false ); - } - else if( path.getPathMetric() > bestPath.getPathMetric() ) - { - mMetrics.put( lastNode.getState(), path ); - - bestPath.setSurvivor( false ); - } - - /* Equal metric paths are allowed to survive */ - } - - /* Capture the best path metric */ - if( path.getPathMetric() > mBestMetric ) - { - mBestMetric = path.getPathMetric(); - } - } - } - - /** - * Path is a sequence of nodes where the starting node always contains - * tribit 0 and constellation 0. Path maintains a running path metric - * derived from the sum of each of the node's branch metrics. - */ - public class Path - { - private boolean mSurvivor = true; - private int mPathMetric = 0; - private ArrayList mNodes = new ArrayList(); - - public Path( Node first ) - { - mNodes.add( first ); - - mPathMetric = first.getBranchMetric(); - - mPathMetrics.evalute( this ); - } - - public Path( ArrayList nodes, int metric ) - { - mNodes = nodes; - mPathMetric = metric; - } - - public List getNodes() - { - return mNodes; - } - - public int getBitErrorCount() - { - return ( getLastNode().getIndex() * 4 ) - mPathMetric; - } - - public void setSurvivor( boolean survivor ) - { - mSurvivor = survivor; - } - - public boolean isSurvivor() - { - return mSurvivor; - } - - public boolean isDead() - { - return !mSurvivor; - } - - @SuppressWarnings( "unchecked" ) - public Path copyOf() - { - return new Path( (ArrayList)mNodes.clone(), mPathMetric ); - } - - /** - * Adds the constellation and returns any new paths created as a result - * of the constellation being detected as an errant constellation. - */ - public void add( Con con ) - { - Node current = getLastNode(); - - Tribit input = null; - - if(current != null) - { - input = INPUT_FROM_CONSTELLATION_MAP.get( con )[ current.getState().getValue() ]; - } - - /* A non-null input tribit from the lookup table is valid, otherwise - * create 8 alternate branches from the original path to explore - * where the error occurred. */ - if( input != null ) - { - add( new Node( mNodes.size(), input, con ) ); - } - else - { - /* Create new paths for each of the valid constellations that - * correspond to the parity of the current state tribit */ - Con[] constellations = current.getState().getType() == - Type.EVEN ? EVEN_CONSTELLATIONS : ODD_CONSTELLATIONS; - - for( int x = 0; x < 8; x++ ) - { - /* Get the input tribit corresponding to the state tribit - * and constellation combination */ - Tribit tribit = INPUT_FROM_CONSTELLATION_MAP - .get( constellations[ x ] )[ current.getState().getValue() ]; - - Node candidate = new Node( mNodes.size(), tribit, constellations[ x ] ); - - /* If the current path metric plus the candidate node's - * branch metric is equal or better than the current best - * metric for the given tribit, add it as a potential path, - * otherwise discard it */ - if( this.getPathMetric() + candidate.getBranchMetric() >= - mPathMetrics.getMetric( tribit ) ) - { - /* If this is the final constellation of the set, simply - * add it to the existing path ... */ - if( x == 7 ) - { - add( candidate ); - } - /* ... otherwise create a copy of the current path */ - else - { - Path path = this.copyOf(); - - path.add( candidate ); - - mNewPaths.add( path ); - } - } - } - } - } - - public void add( Node node ) - { - mNodes.add( node ); - - mPathMetric += node.getBranchMetric(); - - mPathMetrics.evalute( this ); - } - - public int getPathMetric() - { - return mPathMetric; - } - - public Node getLastNode() - { - return mNodes.get( mNodes.size() - 1 ); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "PATH [" ); - sb.append( mNodes.size() ); - sb.append( "-" ); - sb.append( mPathMetric ); - sb.append( "] " ); - - for( Node node: mNodes ) - { - sb.append( node.getState().name() ); - sb.append( "/" ); - sb.append( node.getConstellation().name() ); - sb.append( " " ); - } - - sb.append( mSurvivor ? "SURVIVOR" : "DEAD" ); - - return sb.toString(); - } - } - - /** - * Trellis node containing the constellation used to produce the node and - * the input tribit for the constellation. - */ - public class Node - { - private int mIndex; - private int mBranchMetric; - private Tribit mTribit; - private Con mConstellation; - - public Node( int index, Tribit tribit, Con constellation ) - { - mIndex = index; - mTribit = tribit; - mConstellation = constellation; - - if( mIndex > 0 ) - { - Con transmitted = mTransmittedConstellations.get( index - 1 ); - - if( transmitted == null ) - { - throw new IllegalArgumentException( "Cannot calculate " - + "metric - transmitted constellation symbol is null" ); - } - - mBranchMetric = transmitted.getMetricTo( constellation ); - } - } - - public int getIndex() - { - return mIndex; - } - - public Tribit getState() - { - return mTribit; - } - - public Con getConstellation() - { - return mConstellation; - } - - public int getBranchMetric() - { - return mBranchMetric; - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Node " ); - sb.append( mIndex ); - sb.append( " Metric:" ); - sb.append( mBranchMetric ); - sb.append( " Con:" ); - sb.append( mConstellation.name() ); - sb.append( " State:" ); - if( mTribit == null ) - { - sb.append( "null" ); - } - else - { - sb.append( mTribit.name() ); - } - - return sb.toString(); - } - } - - /** - * Constellation Type. Constellations are classified as even or add based - * on the the composition/comparison of the transmitted (representative) - * dibits: - * - * EVEN: - first dibit differs from second dibit in exactly one bit position - * - * ODD: - first dibit differs from second dibit in zero or two bit positions - * following three patterns: - * 1) repeat (10 10), mirror (01 10), or invert(00 11) - */ - public enum Type { EVEN, ODD }; - - /** - * Constellations, ordered by transmitted value. Transmitted value is the - * value of the dibit pair that represents the constellation when transmitted. - */ - public enum Con - { - CB( 0 ), - CC( 1 ), - C0( 2 ), - C7( 3 ), - CE( 4 ), - C9( 5 ), - C5( 6 ), - C2( 7 ), - CA( 8 ), - CD( 9 ), - C1( 10 ), - C6( 11 ), - CF( 12 ), - C8( 13 ), - C4( 14 ), - C3( 15 ); - - private int mTransmittedValue; - - private Con( int transmittedValue ) - { - mTransmittedValue = transmittedValue; - } - - public int getTransmittedValue() - { - return mTransmittedValue; - } - - public static Con fromTransmittedValue( int value ) - { - if( 0 <= value && value <= 15 ) - { - return values()[ value ]; - } - - return null; - } - - public static Con fromTransmittedDibits( Dibit left, Dibit right ) - { - return fromTransmittedValue( left.getHighValue() + right.getLowValue() ); - } - - /** - * Returns the metric or hamming distance to the other constellation - * using the values from the constellation costs table. A perfect match - * has a metric of 4 and a complete miss has a value of 0. - */ - public int getMetricTo( Con other ) - { - return CONSTELLATION_METRICS[ getTransmittedValue() ][ other.getTransmittedValue() ]; - } - - } - - /** - * Tribit. Used to define both the state and the input bit sequences that - * produce the transmitted constellation bit sequences. Each tribit is - * either even or odd valued producing a direct correlation to the types - * of valid constellations containing the tribit as the state tribit. - */ - public enum Tribit - { - T0( 0, Type.EVEN ), - T1( 1, Type.EVEN ), - T2( 2, Type.ODD ), - T3( 3, Type.ODD ), - T4( 4, Type.ODD ), - T5( 5, Type.ODD ), - T6( 6, Type.EVEN ), - T7( 7, Type.EVEN ); - - private int mValue; - private Type mType; - - private Tribit( int value, Type type ) - { - mValue = value; - mType = type; - } - - public Type getType() - { - return mType; - } - - public int getValue() - { - return mValue; - } - } - - /** - * Test harness - */ - public static void main( String[] args ) - { - String raw = "12:58:51.635 DEBUG dsp.fsk.P25MessageFramer - AFTER DEINTERLEAVE: 00100110000011001010010101111101000101010110100111111100101011000111011011000110000000000000000000000111111101011000001000001111000110000000000010101011110110010000000001011011000000010100011110110001000010100100011111000000000100000000000000000000010000000000001100000000000000011010101010101010101010100010001010000110010111111101101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000010111001001000011100101011010101010101001010011"; - - BinaryMessage message = BinaryMessage.load( raw ); - - mLog.debug( "MSG: " + message.toString() ); - - Trellis_3_4_Rate t = new Trellis_3_4_Rate(); - t.decode( message, 0, 196 ); - - mLog.debug( "DEC: " + message.toString() ); - - mLog.debug( "Finished!" ); - } - - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java index 8578745c..be9ddf16 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java @@ -15,15 +15,12 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.p25.audio; +import io.github.dsheirer.audio.AbstractAudioModule; import io.github.dsheirer.audio.AudioFormats; -import io.github.dsheirer.audio.IAudioPacketProvider; -import io.github.dsheirer.audio.squelch.ISquelchStateListener; import io.github.dsheirer.audio.squelch.SquelchState; -import io.github.dsheirer.channel.metadata.Metadata; import io.github.dsheirer.dsp.gain.NonClippingGain; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessageListener; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.module.Module; import io.github.dsheirer.module.decode.p25.message.hdu.HDUMessage; import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; import io.github.dsheirer.module.decode.p25.message.ldu.LDU2Message; @@ -36,11 +33,9 @@ import jmbe.iface.AudioConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class P25AudioModule extends Module implements Listener, IAudioPacketProvider, IMessageListener, - ISquelchStateListener +public class P25AudioModule extends AbstractAudioModule implements Listener, IMessageListener { - private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class); - + private static final Logger mLog = LoggerFactory.getLogger(P25AudioModule.class); private static final String IMBE_CODEC = "IMBE"; private static boolean mLibraryLoadStatusLogged = false; @@ -49,21 +44,18 @@ public class P25AudioModule extends Module implements Listener, IAudioP private boolean mEncryptedCallStateEstablished = false; private AudioConverter mAudioConverter; - private Listener mAudioPacketListener; private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f); - private Metadata mMetadata; private LDU1Message mCachedLDU1Message = null; private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("P25AudioModule"); - public P25AudioModule(Metadata metadata) + public P25AudioModule() { - mMetadata = metadata; loadConverter(); } @Override - public Listener getMessageListener() + public Listener getMessageListener() { return this; } @@ -83,6 +75,7 @@ public class P25AudioModule extends Module implements Listener, IAudioP @Override public void reset() { + getIdentifierCollection().clear(); } @Override @@ -94,7 +87,15 @@ public class P25AudioModule extends Module implements Listener, IAudioP @Override public void stop() { - + if(hasAudioPacketListener()) + { + ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer(); + endAudioPacket.resetAttributes(); + endAudioPacket.setAudioChannelId(getAudioChannelId()); + endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf()); + endAudioPacket.incrementUserCount(); + getAudioPacketListener().receive(endAudioPacket); + } } /** @@ -104,15 +105,15 @@ public class P25AudioModule extends Module implements Listener, IAudioP * LDU2 message is received and the encryption state can be determined. Both the LDU1 and the LDU2 message are * then processed for audio if the call is unencrypted. */ - public void receive(Message message) + public void receive(IMessage message) { - if(mCanConvertAudio && mAudioPacketListener != null) + if(mCanConvertAudio && hasAudioPacketListener()) { if(mEncryptedCallStateEstablished) { if(message instanceof LDUMessage) { - processAudio((LDUMessage)message); + processAudio((LDUMessage) message); } } else @@ -120,19 +121,19 @@ public class P25AudioModule extends Module implements Listener, IAudioP if(message instanceof HDUMessage) { mEncryptedCallStateEstablished = true; - mEncryptedCall = ((HDUMessage)message).isEncryptedAudio(); + mEncryptedCall = ((HDUMessage) message).getHeaderData().isEncryptedAudio(); } else if(message instanceof LDU1Message) { - //When we receive an LDU1 message with first receiving the HDU message, cache the LDU1 Message + //When we receive an LDU1 message without first receiving the HDU message, cache the LDU1 Message //until we can determine the encrypted call state from the next LDU2 message - mCachedLDU1Message = (LDU1Message)message; + mCachedLDU1Message = (LDU1Message) message; } else if(message instanceof LDU2Message) { mEncryptedCallStateEstablished = true; - LDU2Message ldu2 = (LDU2Message)message; - mEncryptedCall = ldu2.isEncryptedAudio(); + LDU2Message ldu2 = (LDU2Message) message; + mEncryptedCall = ldu2.getEncryptionSyncParameters().isEncryptedAudio(); if(mCachedLDU1Message != null) { @@ -160,10 +161,12 @@ public class P25AudioModule extends Module implements Listener, IAudioP audio = mGain.apply(audio); ReusableAudioPacket audioPacket = mAudioPacketQueue.getBuffer(audio.length); - audioPacket.setMetadata(mMetadata.copyOf()); + audioPacket.resetAttributes(); + audioPacket.setAudioChannelId(getAudioChannelId()); + audioPacket.setIdentifierCollection(getIdentifierCollection().copyOf()); audioPacket.loadAudioFrom(audio); - mAudioPacketListener.receive(audioPacket); + getAudioPacketListener().receive(audioPacket); } } else @@ -186,10 +189,10 @@ public class P25AudioModule extends Module implements Listener, IAudioP @SuppressWarnings("rawtypes") Class temp = Class.forName("jmbe.JMBEAudioLibrary"); - library = (AudioConversionLibrary)temp.newInstance(); + library = (AudioConversionLibrary) temp.newInstance(); if((library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 && - library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1) + library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1) { mAudioConverter = library.getAudioConverter(IMBE_CODEC, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO); @@ -244,24 +247,12 @@ public class P25AudioModule extends Module implements Listener, IAudioP if(!mLibraryLoadStatusLogged) { mLog.error("Couldn't load JMBE audio conversion library due to " - + "security restrictions"); + + "security restrictions"); mLibraryLoadStatusLogged = true; } } } - @Override - public void setAudioPacketListener(Listener listener) - { - mAudioPacketListener = listener; - } - - @Override - public void removeAudioPacketListener() - { - mAudioPacketListener = null; - } - /** * Wrapper for squelch state to process end of call actions. At call end the encrypted call state established * flag is reset so that the encrypted audio state for the next call can be properly detected and we send an @@ -274,12 +265,14 @@ public class P25AudioModule extends Module implements Listener, IAudioP { if(state == SquelchState.SQUELCH) { - if(mAudioPacketListener != null) + if(hasAudioPacketListener()) { ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer(); - endAudioPacket.setMetadata(mMetadata.copyOf()); + endAudioPacket.resetAttributes(); + endAudioPacket.setAudioChannelId(getAudioChannelId()); + endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf()); endAudioPacket.incrementUserCount(); - mAudioPacketListener.receive(endAudioPacket); + getAudioPacketListener().receive(endAudioPacket); } mEncryptedCallStateEstablished = false; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Lra.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Lra.java new file mode 100644 index 00000000..4d42499a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Lra.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25Lra extends IntegerIdentifier +{ + public APCO25Lra(int value) + { + super(value, IdentifierClass.NETWORK, Form.LOCATION_REGISTRATION_AREA, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 identifier + */ + public static Identifier create(int lra) + { + return new APCO25Lra(lra); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Nac.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Nac.java new file mode 100644 index 00000000..e5227844 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Nac.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25Nac extends IntegerIdentifier +{ + public APCO25Nac(int value) + { + super(value, IdentifierClass.NETWORK, Form.NETWORK_ACCESS_CODE, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + @Override + public boolean isValid() + { + return getValue() > 0; + } + + /** + * Creates a new APCO-25 identifier + */ + public static Identifier create(int nac) + { + return new APCO25Nac(nac); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Rfss.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Rfss.java new file mode 100644 index 00000000..10d8f9a1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Rfss.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25Rfss extends IntegerIdentifier +{ + public APCO25Rfss(int value) + { + super(value, IdentifierClass.NETWORK, Form.RF_SUBSYSTEM, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 identifier + */ + public static Identifier create(int rfss) + { + return new APCO25Rfss(rfss); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Site.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Site.java new file mode 100644 index 00000000..72cd8268 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Site.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25Site extends IntegerIdentifier +{ + public APCO25Site(int value) + { + super(value, IdentifierClass.NETWORK, Form.SITE, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 site identifier + */ + public static Identifier create(int site) + { + return new APCO25Site(site); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25System.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25System.java new file mode 100644 index 00000000..6eb0d6d3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25System.java @@ -0,0 +1,48 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25System extends IntegerIdentifier +{ + public APCO25System(int value) + { + super(value, IdentifierClass.NETWORK, Form.SYSTEM, Role.BROADCAST); + } + + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 identifier + */ + public static Identifier create(int system) + { + return new APCO25System(system); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Wacn.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Wacn.java new file mode 100644 index 00000000..b65330f3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/APCO25Wacn.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25Wacn extends IntegerIdentifier +{ + public APCO25Wacn(int value) + { + super(value, IdentifierClass.NETWORK, Form.WACN, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 identifier + */ + public static Identifier create(int wacn) + { + return new APCO25Wacn(wacn); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25Channel.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25Channel.java new file mode 100644 index 00000000..e87dffe8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25Channel.java @@ -0,0 +1,135 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.channel; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.protocol.Protocol; + +/** + * APCO-25 Channel composed of a frequency band and a channel number within the frequency band. + * + * This class supports inserting a band identifier message that allows the uplink and downlink freuqencies + * for the channel to be calculated. + */ +public class APCO25Channel extends Identifier implements IChannelDescriptor, Comparable +{ + /** + * Creates an APCO-25 channel identifier + */ + public APCO25Channel(P25Channel p25Channel) + { + super(p25Channel, IdentifierClass.NETWORK, Form.CHANNEL, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + @Override + public int[] getFrequencyBandIdentifiers() + { + return getValue().getFrequencyBandIdentifiers(); + } + + @Override + public int getTimeslotCount() + { + return getValue().getTimeslotCount(); + } + + @Override + public boolean isTDMAChannel() + { + return getValue().isTDMAChannel(); + } + + public void setFrequencyBand(IFrequencyBand bandIdentifier) + { + getValue().setFrequencyBand(bandIdentifier); + } + + /** + * Downlink frequency for this channel. + * + * @return frequency in hertz + */ + public long getDownlinkFrequency() + { + return getValue().getDownlinkFrequency(); + } + + /** + * Uplink frequency for this channel. + * + * @return frequency in hertz + */ + public long getUplinkFrequency() + { + return getValue().getUplinkFrequency(); + } + + + /** + * Creates a new APCO-25 identifier + */ + public static APCO25Channel create(int frequencyBand, int channelNumber) + { + return new APCO25Channel(new P25Channel(frequencyBand, channelNumber)); + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || !(o instanceof APCO25Channel) || ((APCO25Channel)o).getValue() == null) + { + return false; + } + + return getValue().equals(((APCO25Channel)o).getValue()); + } + + @Override + public int hashCode() + { + return getValue().hashCode(); + } + + @Override + public int compareTo(IChannelDescriptor o) + { + if(o == null) + { + return -1; + } + + return Long.compare(getDownlinkFrequency(), o.getDownlinkFrequency()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25ExplicitChannel.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25ExplicitChannel.java new file mode 100644 index 00000000..a43e0d8f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/APCO25ExplicitChannel.java @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.channel; + +/** + * APCO-25 Channel composed of separate uplink and downlink channel numbers and frequency bands. + * + * This class supports inserting band identifier message(s) that allows the uplink and downlink freuqencies + * for the channel to be calculated. + */ +public class APCO25ExplicitChannel extends APCO25Channel +{ + public APCO25ExplicitChannel(P25ExplicitChannel p25ExplicitChannel) + { + super(p25ExplicitChannel); + } + + /** + * Creates a new APCO-25 explicit channel + */ + public static APCO25ExplicitChannel create(int downlinkFrequencyBand, int downlinkChannelNumber, + int uplinkFrequencyBand, int uplinkChannelNumber) + { + return new APCO25ExplicitChannel(new P25ExplicitChannel(downlinkFrequencyBand, downlinkChannelNumber, + uplinkFrequencyBand, uplinkChannelNumber)); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java new file mode 100644 index 00000000..954d5213 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java @@ -0,0 +1,189 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.identifier.channel; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.protocol.Protocol; + +import java.util.Objects; + +public class P25Channel implements IChannelDescriptor +{ + private IFrequencyBand mFrequencyBand; + private int mBandIdentifier; + private int mChannelNumber; + + public P25Channel(int bandIdentifier, int channelNumber) + { + mBandIdentifier = bandIdentifier; + mChannelNumber = channelNumber; + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Frequency band information that backs this channel + */ + public IFrequencyBand getFrequencyBand() + { + return mFrequencyBand; + } + + public int getDownlinkBandIdentifier() + { + return mBandIdentifier; + } + + public int getDownlinkChannelNumber() + { + return mChannelNumber; + } + + public int getUplinkBandIdentifier() + { + return mBandIdentifier; + } + + public int getUplinkChannelNumber() + { + return mChannelNumber; + } + + @Override + public long getDownlinkFrequency() + { + if(mFrequencyBand != null) + { + return mFrequencyBand.getDownlinkFrequency(getDownlinkChannelNumber()); + } + + return 0; + } + + @Override + public long getUplinkFrequency() + { + if(mFrequencyBand != null) + { + return mFrequencyBand.getUplinkFrequency(getUplinkChannelNumber()); + } + + return 0; + } + + public boolean hasUplinkChannel() + { + return getUplinkChannelNumber() != 4095; + } + + @Override + public int[] getFrequencyBandIdentifiers() + { + if(getDownlinkBandIdentifier() != getUplinkBandIdentifier()) + { + int[] identifiers = new int[2]; + identifiers[0] = getDownlinkBandIdentifier(); + identifiers[1] = getUplinkBandIdentifier(); + return identifiers; + } + else + { + int[] identifiers = new int[1]; + identifiers[0] = getDownlinkBandIdentifier(); + return identifiers; + } + } + + @Override + public void setFrequencyBand(IFrequencyBand frequencyBand) + { + mFrequencyBand = frequencyBand; + } + + @Override + public int getTimeslotCount() + { + if(mFrequencyBand != null) + { + return mFrequencyBand.getTimeslotCount(); + } + + return 0; + } + + @Override + public boolean isTDMAChannel() + { + if(mFrequencyBand != null) + { + return mFrequencyBand.isTDMA(); + } + + return false; + } + + /** + * Formatted channel number + */ + public String toString() + { + if(getDownlinkBandIdentifier() == getUplinkBandIdentifier() && getDownlinkChannelNumber() == getUplinkChannelNumber()) + { + return getDownlinkBandIdentifier() + "-" + getDownlinkChannelNumber(); + } + else if(hasUplinkChannel()) + { + return getDownlinkBandIdentifier() + "-" + getDownlinkChannelNumber() + "/" + + getUplinkBandIdentifier() + "-" + getUplinkChannelNumber(); + } + else + { + return getDownlinkBandIdentifier() + "-" + getDownlinkChannelNumber() + "/-----"; + } + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + P25Channel that = (P25Channel)o; + return mBandIdentifier == that.mBandIdentifier && + mChannelNumber == that.mChannelNumber; + } + + @Override + public int hashCode() + { + return Objects.hash(mBandIdentifier, mChannelNumber); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25ExplicitChannel.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25ExplicitChannel.java new file mode 100644 index 00000000..0b696e2f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25ExplicitChannel.java @@ -0,0 +1,84 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.identifier.channel; + +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; + +public class P25ExplicitChannel extends P25Channel implements Comparable +{ + private IFrequencyBand mUplinkFrequencyBand; + private int mUplinkBandIdentifier; + private int mUplinkChannelNumber; + + public P25ExplicitChannel(int downlinkBandIdentifier, int downlinkChannelNumber, + int uplinkBandIdentifier, int uplinkChannelNumber) + { + super(downlinkBandIdentifier, downlinkChannelNumber); + mUplinkBandIdentifier = uplinkBandIdentifier; + mUplinkChannelNumber = uplinkChannelNumber; + } + + public int getUplinkBandIdentifier() + { + return mUplinkBandIdentifier; + } + + public int getUplinkChannelNumber() + { + return mUplinkChannelNumber; + } + + @Override + public long getUplinkFrequency() + { + if(mUplinkFrequencyBand != null) + { + return mUplinkFrequencyBand.getUplinkFrequency(getUplinkChannelNumber()); + } + + return 0; + } + + @Override + public void setFrequencyBand(IFrequencyBand frequencyBand) + { + if(frequencyBand.getIdentifier() == getUplinkBandIdentifier()) + { + mUplinkFrequencyBand = frequencyBand; + } + + super.setFrequencyBand(frequencyBand); + } + + @Override + public int compareTo(P25Channel other) + { + //Ignores the uplink channel ... for ordering we'll simply order by the downlink channel + if(getDownlinkBandIdentifier() == other.getDownlinkBandIdentifier()) + { + return Integer.compare(getDownlinkChannelNumber(), other.getDownlinkChannelNumber()); + } + else + { + return Integer.compare(getDownlinkBandIdentifier(), other.getDownlinkBandIdentifier()); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/encryption/APCO25EncryptionKey.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/encryption/APCO25EncryptionKey.java new file mode 100644 index 00000000..9c933a75 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/encryption/APCO25EncryptionKey.java @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.encryption; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.encryption.EncryptionKey; +import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25EncryptionKey extends EncryptionKeyIdentifier +{ + public APCO25EncryptionKey(EncryptionKey encryptionKey) + { + super(encryptionKey, IdentifierClass.NETWORK, Form.ENCRYPTION_KEY, Role.BROADCAST); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 encryption algorithm + */ + public static APCO25EncryptionKey create(int alogorithm, int keyId) + { + return new APCO25EncryptionKey(new EncryptionKey(alogorithm, keyId)); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/APCO25IpAddress.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/APCO25IpAddress.java new file mode 100644 index 00000000..89d6195a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/APCO25IpAddress.java @@ -0,0 +1,52 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.identifier.ipv4; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.ipv4.IPV4Address; +import io.github.dsheirer.identifier.ipv4.IPV4Identifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * APCO25 IPV4 Address + */ +public class APCO25IpAddress extends IPV4Identifier +{ + public APCO25IpAddress(IPV4Address ipAddress, Role role) + { + super(ipAddress, role); + } + + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + public static APCO25IpAddress createTo(int value) + { + return new APCO25IpAddress(new IPV4Address(value), Role.TO); + } + + public static APCO25IpAddress createFrom(int value) + { + return new APCO25IpAddress(new IPV4Address(value), Role.FROM); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/message/APCO25ShortDataMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/message/APCO25ShortDataMessage.java new file mode 100644 index 00000000..24dd1428 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/message/APCO25ShortDataMessage.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.message; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * APCO-25 short data message + */ +public class APCO25ShortDataMessage extends IntegerIdentifier +{ + /** + * Constructs an APCO-25 status + * + * @param shortDataMessage value + */ + public APCO25ShortDataMessage(int shortDataMessage) + { + super(shortDataMessage, IdentifierClass.USER, Form.SHORT_DATA_MESSAGE, Role.ANY); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a short data message + * + * @param messageId + */ + public static APCO25ShortDataMessage create(int messageId) + { + return new APCO25ShortDataMessage(messageId); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/patch/APCO25PatchGroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/patch/APCO25PatchGroup.java new file mode 100644 index 00000000..95a1bdfe --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/patch/APCO25PatchGroup.java @@ -0,0 +1,46 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.patch; + +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.patch.PatchGroupIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * APCO25 Patch Group + */ +public class APCO25PatchGroup extends PatchGroupIdentifier +{ + public APCO25PatchGroup(PatchGroup value) + { + super(value); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + public static APCO25PatchGroup create(PatchGroup patchGroup) + { + return new APCO25PatchGroup(patchGroup); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/status/APCO25Status.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/status/APCO25Status.java new file mode 100644 index 00000000..22b5b4d2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/status/APCO25Status.java @@ -0,0 +1,69 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.status; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * APCO-25 Unit or User status + */ +public class APCO25Status extends IntegerIdentifier +{ + private Role mRole; + + /** + * Constructs an APCO-25 status + * @param status value + * @param role of the user/unit that the status applies to + */ + public APCO25Status(int status, IdentifierClass identifierClass, Form form, Role role) + { + super(status, identifierClass, form, role); + mRole = role; + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a unit status + * @param status + * @return + */ + public static APCO25Status createUnitStatus(int status) + { + return new APCO25Status(status, IdentifierClass.USER, Form.UNIT_STATUS, Role.STATUS); + } + + /** + * Creates a User status + */ + public static APCO25Status createUserStatus(int status) + { + return new APCO25Status(status, IdentifierClass.USER, Form.USER_STATUS, Role.STATUS); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnnouncementTalkgroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnnouncementTalkgroup.java new file mode 100644 index 00000000..9dad5d83 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnnouncementTalkgroup.java @@ -0,0 +1,47 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.talkgroup; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.Role; + +/** + * APCO25 Announcement Talkgroup - grouping of talkgroups within a system. + */ +public class APCO25AnnouncementTalkgroup extends APCO25Talkgroup +{ + /** + * Constructs an APCO25 Talkgroup Identifier with a FROM role. + * + * @param value of the talkgroup + */ + public APCO25AnnouncementTalkgroup(Integer value) + { + super(value, Role.BROADCAST, true); + } + + /** + * Creates an APCO-25 announcement group talkgroup identifier + */ + public static Identifier create(int talkgroup) + { + return new APCO25AnnouncementTalkgroup(talkgroup); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnyTalkgroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnyTalkgroup.java new file mode 100644 index 00000000..60768302 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25AnyTalkgroup.java @@ -0,0 +1,47 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.talkgroup; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; + +/** + * APCO25 Talkgroup Identifier with an ANY role. + */ +public class APCO25AnyTalkgroup extends APCO25Talkgroup +{ + /** + * Constructs an APCO25 Talkgroup Identifier with an ANY role. + * + * @param value of the talkgroup + */ + public APCO25AnyTalkgroup(Integer value) + { + super(value, Role.ANY, true); + } + + /** + * Creates an ANY APCO-25 talkgroup identifier + */ + public static TalkgroupIdentifier create(int talkgroup) + { + return new APCO25AnyTalkgroup(talkgroup); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25FromTalkgroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25FromTalkgroup.java new file mode 100644 index 00000000..7de37e91 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25FromTalkgroup.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.talkgroup; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; + +/** + * APCO25 Talkgroup Identifier with a FROM role. + */ +public class APCO25FromTalkgroup extends APCO25Talkgroup +{ + public APCO25FromTalkgroup(Integer value, boolean isGroup) + { + super(value, Role.FROM, isGroup); + } + + /** + * Creates an individual FROM APCO-25 talkgroup identifier + */ + public static TalkgroupIdentifier createIndividual(int talkgroup) + { + return new APCO25FromTalkgroup(talkgroup, false); + } + + /** + * Creates a FROM APCO-25 talkgroup identifier + */ + public static TalkgroupIdentifier createGroup(int talkgroup) + { + return new APCO25FromTalkgroup(talkgroup, true); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25IdentifierTalkgroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25IdentifierTalkgroup.java new file mode 100644 index 00000000..4b867db3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25IdentifierTalkgroup.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.talkgroup; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; + +/** + * APCO25 Talkgroup Identifier with a FROM role. + */ +public class APCO25IdentifierTalkgroup extends APCO25Talkgroup +{ + public APCO25IdentifierTalkgroup(Integer value, boolean isGroup) + { + super(value, Role.IDENTIFIER, isGroup); + } + + /** + * Creates an individual FROM APCO-25 talkgroup identifier + */ + public static TalkgroupIdentifier createIndividual(int talkgroup) + { + return new APCO25IdentifierTalkgroup(talkgroup, false); + } + + /** + * Creates a FROM APCO-25 talkgroup identifier + */ + public static TalkgroupIdentifier createGroup(int talkgroup) + { + return new APCO25IdentifierTalkgroup(talkgroup, true); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25Talkgroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25Talkgroup.java new file mode 100644 index 00000000..4972d59c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25Talkgroup.java @@ -0,0 +1,39 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.identifier.talkgroup; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25Talkgroup extends TalkgroupIdentifier +{ + public APCO25Talkgroup(Integer value, Role role, boolean isGroup) + { + super(value, role, isGroup); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25ToTalkgroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25ToTalkgroup.java new file mode 100644 index 00000000..93065781 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/talkgroup/APCO25ToTalkgroup.java @@ -0,0 +1,50 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.talkgroup; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; + +/** + * APCO25 Talkgroup Identifier with a FROM role. + */ +public class APCO25ToTalkgroup extends APCO25Talkgroup +{ + public APCO25ToTalkgroup(Integer value, boolean isGroup) + { + super(value, Role.TO, isGroup); + } + + /** + * Creates a TO APCO-25 group talkgroup identifier + */ + public static TalkgroupIdentifier createGroup(int talkgroup) + { + return new APCO25ToTalkgroup(talkgroup, true); + } + + /** + * Creates a TO APCO-25 individual talkgroup identifier + */ + public static TalkgroupIdentifier createIndividual(int talkgroup) + { + return new APCO25ToTalkgroup(talkgroup, false); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/telephone/APCO25TelephoneNumber.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/telephone/APCO25TelephoneNumber.java new file mode 100644 index 00000000..1350da92 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/telephone/APCO25TelephoneNumber.java @@ -0,0 +1,65 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.identifier.telephone; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.string.StringIdentifier; +import io.github.dsheirer.protocol.Protocol; + +public class APCO25TelephoneNumber extends StringIdentifier +{ + public APCO25TelephoneNumber(String telephoneNumber, Role role) + { + super(telephoneNumber, IdentifierClass.USER, Form.TELEPHONE_NUMBER, role); + } + + @Override + public Protocol getProtocol() + { + return Protocol.APCO25; + } + + /** + * Creates a new APCO-25 telephone number + */ + public static Identifier createFrom(String telephoneNumber) + { + return new APCO25TelephoneNumber(telephoneNumber, Role.FROM); + } + + /** + * Creates a new APCO-25 telephone number + */ + public static Identifier createTo(String telephoneNumber) + { + return new APCO25TelephoneNumber(telephoneNumber, Role.TO); + } + + /** + * Creates a new APCO-25 telephone number + */ + public static Identifier createAny(String telephoneNumber) + { + return new APCO25TelephoneNumber(telephoneNumber, Role.ANY); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/IAdjacentSite.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/IAdjacentSite.java index afd06a5e..7d068e85 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/IAdjacentSite.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/IAdjacentSite.java @@ -1,20 +1,24 @@ package io.github.dsheirer.module.decode.p25.message; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; + /** * Interface for adjacent site (ie neighbor) messages */ public interface IAdjacentSite { - public String getUniqueID(); - public String getRFSS(); - public String getSystemID(); - public String getSiteID(); - public String getLRA(); - public String getSystemServiceClass(); - - public String getDownlinkChannel(); - public long getDownlinkFrequency(); - - public String getUplinkChannel(); - public long getUplinkFrequency(); + String getUniqueID(); + + Identifier getRFSSId(); + + Identifier getSystemID(); + + Identifier getSiteID(); + + Identifier getLRAId(); + + String getSystemServiceClass(); + + IChannelDescriptor getChannel(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/IBandIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/IBandIdentifier.java deleted file mode 100644 index 3471efba..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/IBandIdentifier.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message; - -public interface IBandIdentifier -{ - - /** - * (Band) Identifier - */ - public abstract int getIdentifier(); - - /** - * Channel spacing in hertz - */ - public abstract long getChannelSpacing(); - - /** - * Base frequency in hertz - */ - public abstract long getBaseFrequency(); - - - /** - * Channel bandwidth in hertz - */ - public abstract int getBandwidth(); - - /** - * Transmit offset in hertz - */ - public abstract long getTransmitOffset(); - - /** - * Downlink (tower to mobile) Frequency for the specified channel - */ - public abstract long getDownlinkFrequency(int channelNumber); - - /** - * Uplink (mobile to tower) Frequency for the specified channel - */ - public abstract long getUplinkFrequency(int channelNumber); - - /** - * Indicates if this band is an FDMA (false) or TDMA (true) band - */ - public boolean isTDMA(); -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBand.java new file mode 100644 index 00000000..6ebd3fbe --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBand.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.message; + +public interface IFrequencyBand +{ + /** + * (Band) Identifier + */ + public abstract int getIdentifier(); + + /** + * Channel spacing in hertz + */ + public abstract long getChannelSpacing(); + + /** + * Base frequency in hertz + */ + public abstract long getBaseFrequency(); + + + /** + * Channel bandwidth in hertz + */ + public abstract int getBandwidth(); + + /** + * Transmit offset in hertz + */ + public abstract long getTransmitOffset(); + + /** + * Downlink (tower to mobile) Frequency for the specified channel + */ + public abstract long getDownlinkFrequency(int channelNumber); + + /** + * Uplink (mobile to tower) Frequency for the specified channel + */ + public abstract long getUplinkFrequency(int channelNumber); + + /** + * Indicates if this band is an FDMA (false) or TDMA (true) band + */ + public boolean isTDMA(); + + /** + * Number of timeslots available on this channel + */ + public int getTimeslotCount(); +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBandReceiver.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBandReceiver.java new file mode 100644 index 00000000..1c5c2441 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/IFrequencyBandReceiver.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.message; + + +import io.github.dsheirer.channel.IChannelDescriptor; + +import java.util.List; + +/** + * Interface to allow messages to be augmented with IdentiferUpdateXXX type + * messages that provide the channel information necessary to calculate the + * uplink and downlink frequency for the channel. + */ +public interface IFrequencyBandReceiver +{ + /** + * List of APCO-25 channels provided by the message. Channels may be enriched with frequency band + * details by an external provider. + * + * @return list of channels + */ + List getChannels(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/IdentifierReceiver.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/IdentifierReceiver.java deleted file mode 100644 index af1c6313..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/IdentifierReceiver.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message; - - -/** - * Interface to allow messages to be augmented with IdentiferUpdateXXX type - * messages that provide the channel information necessary to calculate the - * uplink and downlink frequency for the channel. - */ -public interface IdentifierReceiver -{ - public void setIdentifierMessage( int identifier, IBandIdentifier message ); - - public int[] getIdentifiers(); -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java index 594c510b..b00bbf5d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java @@ -18,81 +18,97 @@ ******************************************************************************/ package io.github.dsheirer.module.decode.p25.message; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Nac; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.protocol.Protocol; -import java.text.SimpleDateFormat; - -public class P25Message extends Message +public abstract class P25Message extends Message { - public enum DuplexMode {HALF, FULL}; - public enum SessionMode {PACKET, CIRCUIT}; - - public static final int[] NAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; - public static final int[] DUID = {12, 13, 14, 15}; - public static final int[] BCH = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; - - protected SimpleDateFormat mTimeDurationFormat = new SimpleDateFormat("HH:mm:ss.SSS"); - - protected BinaryMessage mMessage; - protected DataUnitID mDUID; - protected AliasList mAliasList; - - protected CRC[] mCRC; - - public P25Message(BinaryMessage message, DataUnitID duid, AliasList aliasList) + public enum DuplexMode { - super(); + HALF, FULL + } + public enum SessionMode + { + PACKET, CIRCUIT + } + + private CorrectedBinaryMessage mMessage; + private boolean mValid = true; + private Identifier mNAC; + + /** + * Constructs a P25 message. + * + * @param message containing the binary message and optional corrected bit count + * @param nac Network Access Code (NAC) for the message + * @param timestamp when the message was transmitted + */ + public P25Message(CorrectedBinaryMessage message, int nac, long timestamp) + { + super(timestamp); mMessage = message; - mDUID = duid; - mAliasList = aliasList; + mNAC = APCO25Nac.create(nac); } - public CRC[] getCRCResults() + /** + * Constructs a P25 message using current system time for when the message was transmitted. + * + * @param message containing the binary message and optional corrected bit count + * @param nac Network Access Code (NAC) for the message + */ + public P25Message(CorrectedBinaryMessage message, int nac) { - return mCRC; + this(message, nac, System.currentTimeMillis()); } - public BinaryMessage getSourceMessage() + /** + * Constructs a P25 message where the binary message will be created/updated after construction. + * @param nac Network Access Code (NAC) for the message + * @param timestamp when the message was transmitted + */ + protected P25Message(int nac, long timestamp) + { + super(timestamp); + mNAC = APCO25Nac.create(nac); + } + + /** + * Establishes the message binary sequence. + */ + protected void setMessage(CorrectedBinaryMessage message) + { + mMessage = message; + } + + /** + * The transmitted binary message + */ + protected CorrectedBinaryMessage getMessage() { return mMessage; } - public AliasList getAliasList() + public Identifier getNAC() { - return mAliasList; + return mNAC; } - public String getNAC() + /** + * Data Unit ID indicates the type of P25 message + */ + public abstract DataUnitID getDUID(); + + public String toString() { - return mMessage.getHex(NAC, 3); + return getMessageStub(); } - public DataUnitID getDUID() - { - return mDUID; - } - - @Override - public String getErrorStatus() - { - if(mCRC == null) - { - return "UNKNOWN"; - } - - return CRC.format(mCRC); - } - - @Override - public String getMessage() + protected String getMessageStub() { StringBuilder sb = new StringBuilder(); @@ -100,89 +116,30 @@ public class P25Message extends Message sb.append(getNAC()); sb.append(" "); sb.append(getDUID().getLabel()); - sb.append(" DATA UNIT ID:" + mMessage.getInt(DUID)); return sb.toString(); } @Override - public String getBinaryMessage() + public Protocol getProtocol() { - return mMessage.toString(); - } - - @Override - public String getProtocol() - { - return "P25 Phase 1"; - } - - @Override - public String getEventType() - { - return mDUID.name(); - } - - @Override - public String getFromID() - { - return null; - } - - @Override - public Alias getFromIDAlias() - { - if(mAliasList != null) - { - return mAliasList.getTalkgroupAlias(getFromID()); - } - - return null; - } - - @Override - public String getToID() - { - return null; - } - - @Override - public Alias getToIDAlias() - { - if(mAliasList != null) - { - return mAliasList.getTalkgroupAlias(getToID()); - } - - return null; - } - - @Override - public Plottable getPlottable() - { - return null; - } - - @Override - public String toString() - { - return getMessage(); + return Protocol.APCO25; } + /** + * Indicates if this message is valid and has passed all error detection and correction routines. + */ @Override public boolean isValid() { - if(mCRC != null) - { - for(CRC crc : mCRC) - { - if(crc == CRC.FAILED_CRC) - { - return false; - } - } - } + return mValid; + } - return true; + /** + * Sets this message to the valid/invalid state. The default state is valid (true). + */ + protected void setValid(boolean valid) + { + mValid = valid; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/P25MessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/P25MessageFactory.java new file mode 100644 index 00000000..cb213945 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/P25MessageFactory.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.message.hdu.HDUMessage; +import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; +import io.github.dsheirer.module.decode.p25.message.ldu.LDU2Message; +import io.github.dsheirer.module.decode.p25.message.tdu.TDULinkControlMessage; +import io.github.dsheirer.module.decode.p25.message.tdu.TDUMessage; +import io.github.dsheirer.module.decode.p25.message.vselp.VSELP1Message; +import io.github.dsheirer.module.decode.p25.message.vselp.VSELP2Message; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory for creating P25 message parser instances. + */ +public class P25MessageFactory +{ + private final static Logger mLog = LoggerFactory.getLogger(P25MessageFactory.class); + + /** + * Creates a P25 message for known message types. + * + * NOTE: TSBK and PDU messages are not processed by this factory class. + * + * @param dataUnitID that identifies the message type + * @param nac decoded from the network identifier (NID) + * @param timestamp of the message + * @param message containing message bits and bit error count results from nid error detection and + * correction + * @return constructed message parser + */ + public static P25Message create(DataUnitID dataUnitID, int nac, long timestamp, CorrectedBinaryMessage message) + { + switch(dataUnitID) + { + case HEADER_DATA_UNIT: + return new HDUMessage(message, nac, timestamp); + case LOGICAL_LINK_DATA_UNIT_1: + return new LDU1Message(message, nac, timestamp); + case LOGICAL_LINK_DATA_UNIT_2: + return new LDU2Message(message, nac, timestamp); + case PACKET_HEADER_DATA_UNIT: + mLog.warn("WARNING: PDU messages must be created by the PDUMessageFactory"); + return null; + case TERMINATOR_DATA_UNIT: + return new TDUMessage(message, nac, timestamp); + case TERMINATOR_DATA_UNIT_LINK_CONTROL: + return new TDULinkControlMessage(message, nac, timestamp); + case TRUNKING_SIGNALING_BLOCK_1: + case TRUNKING_SIGNALING_BLOCK_2: + case TRUNKING_SIGNALING_BLOCK_3: + mLog.warn("WARNING: TSBK messages must be created by the TSBKMessageFactory"); + return null; + case VSELP1: + return new VSELP1Message(message, nac, timestamp); + case VSELP2: + return new VSELP2Message(message, nac, timestamp); + default: + return new UnknownP25Message(message, nac, timestamp, dataUnitID); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/UnknownP25Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/UnknownP25Message.java new file mode 100644 index 00000000..3b6ac01d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/UnknownP25Message.java @@ -0,0 +1,63 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * P25 Message with unknown or unrecognized data unit id + */ +public class UnknownP25Message extends P25Message +{ + private DataUnitID mDataUnitID; + + public UnknownP25Message(CorrectedBinaryMessage message, int nac, long timestamp, DataUnitID dataUnitID) + { + super(message, nac); + mDataUnitID = dataUnitID; + } + + @Override + public DataUnitID getDUID() + { + return mDataUnitID; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/AMBTCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/AMBTCMessageFilter.java new file mode 100644 index 00000000..233f34e5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/AMBTCMessageFilter.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.Collections; +import java.util.List; + +public class AMBTCMessageFilter extends Filter +{ + public AMBTCMessageFilter() + { + super("Alternate Multi-Block Trunking Control"); + } + + @Override + public boolean passes(IMessage message) + { + return mEnabled && canProcess(message); + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof AMBTCMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/HDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/HDUMessageFilter.java index 0ea7fd80..5e7d88d3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/HDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/HDUMessageFilter.java @@ -2,34 +2,34 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.message.hdu.HDUMessage; import java.util.Collections; import java.util.List; -public class HDUMessageFilter extends Filter +public class HDUMessageFilter extends Filter { - public HDUMessageFilter() - { - super( "Header Data Unit" ); - } - - @Override - public boolean passes( Message message ) + public HDUMessageFilter() { - return mEnabled && canProcess( message ); + super("Header Data Unit"); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof HDUMessage; + return mEnabled && canProcess(message); } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof HDUMessage; + } + + @Override public List> getFilterElements() { - return Collections.EMPTY_LIST; + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/IPPacketMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/IPPacketMessageFilter.java new file mode 100644 index 00000000..17bf3ef1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/IPPacketMessageFilter.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.PacketMessage; + +import java.util.Collections; +import java.util.List; + +public class IPPacketMessageFilter extends Filter +{ + public IPPacketMessageFilter() + { + super("Packet Message"); + } + + @Override + public boolean passes(IMessage message) + { + return mEnabled && canProcess(message); + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof PacketMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/LDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/LDUMessageFilter.java index b94a1232..febf1a91 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/LDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/LDUMessageFilter.java @@ -2,34 +2,34 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.message.ldu.LDUMessage; import java.util.Collections; import java.util.List; -public class LDUMessageFilter extends Filter +public class LDUMessageFilter extends Filter { - public LDUMessageFilter() - { - super( "Link Data Unit" ); - } - - @Override - public boolean passes( Message message ) + public LDUMessageFilter() { - return mEnabled && canProcess( message ); + super("Link Data Unit"); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof LDUMessage; + return mEnabled && canProcess(message); } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof LDUMessage; + } + + @Override public List> getFilterElements() { - return Collections.EMPTY_LIST; + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/P25MessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/P25MessageFilterSet.java index 83c50038..8f6993bb 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/P25MessageFilterSet.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/P25MessageFilterSet.java @@ -1,26 +1,32 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.message.SyncLossMessage; import io.github.dsheirer.module.decode.p25.message.P25Message; -public class P25MessageFilterSet extends FilterSet +public class P25MessageFilterSet extends FilterSet { - public P25MessageFilterSet() - { - super( "P25 Message Filter" ); - - addFilter( new HDUMessageFilter() ); - addFilter( new LDUMessageFilter() ); - addFilter( new PDUMessageFilter() ); - addFilter( new TDUMessageFilter() ); - addFilter( new TDULCMessageFilter() ); - addFilter( new TSBKMessageFilterSet() ); - } - - @Override - public boolean canProcess( Message message ) + public P25MessageFilterSet() { - return message instanceof P25Message; + super("P25 Message Filter"); + + addFilter(new AMBTCMessageFilter()); + addFilter(new HDUMessageFilter()); + addFilter(new IPPacketMessageFilter()); + addFilter(new LDUMessageFilter()); + addFilter(new PDUMessageFilter()); + addFilter(new SNDCPMessageFilter()); + addFilter(new SyncLossMessageFilter()); + addFilter(new TDUMessageFilter()); + addFilter(new TDULCMessageFilter()); + addFilter(new TSBKMessageFilterSet()); + addFilter(new UMBTCMessageFilter()); + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof P25Message || message instanceof SyncLossMessage; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/PDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/PDUMessageFilter.java index e5a6ad76..7ea842c0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/PDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/PDUMessageFilter.java @@ -2,53 +2,53 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.Opcode; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; -public class PDUMessageFilter extends Filter +public class PDUMessageFilter extends Filter { - private HashMap> mOpcodeFilterElements = - new HashMap>(); - - public PDUMessageFilter() - { - super( "PDU - Packet Data Unit" ); - - for( Opcode opcode: Opcode.values() ) - { - mOpcodeFilterElements.put( opcode, new FilterElement( opcode ) ); - } - } - - @Override - public boolean passes( Message message ) - { - if( mEnabled && message instanceof PDUMessage) - { - PDUMessage pdu = (PDUMessage)message; - - Opcode opcode = pdu.getOpcode(); - - return mOpcodeFilterElements.get( opcode ).isEnabled(); - } + private Map> mOpcodeFilterElements = new HashMap>(); - return false; + public PDUMessageFilter() + { + super("PDU - Packet Data Unit"); + + for(Opcode opcode : Opcode.values()) + { + mOpcodeFilterElements.put(opcode, new FilterElement(opcode)); + } } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof PDUMessage; + if(mEnabled && message instanceof PDUMessage) + { + PDUMessage pdu = (PDUMessage) message; + + Opcode opcode = pdu.getOpcode(); + + return mOpcodeFilterElements.get(opcode).isEnabled(); + } + + return false; } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof PDUMessage; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mOpcodeFilterElements.values() ); + return new ArrayList>(mOpcodeFilterElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SNDCPMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SNDCPMessageFilter.java new file mode 100644 index 00000000..54e7e621 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SNDCPMessageFilter.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPMessage; + +import java.util.Collections; +import java.util.List; + +public class SNDCPMessageFilter extends Filter +{ + public SNDCPMessageFilter() + { + super("Sub-Network Dependent Convergence Protocol"); + } + + @Override + public boolean passes(IMessage message) + { + return mEnabled && canProcess(message); + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof SNDCPMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SyncLossMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SyncLossMessageFilter.java new file mode 100644 index 00000000..d32c0aac --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/SyncLossMessageFilter.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.message.SyncLossMessage; + +import java.util.Collections; +import java.util.List; + +public class SyncLossMessageFilter extends Filter +{ + public SyncLossMessageFilter() + { + super("Sync Loss"); + } + + @Override + public boolean passes(IMessage message) + { + return mEnabled && canProcess(message); + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof SyncLossMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDULCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDULCMessageFilter.java index b123fabf..7266535c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDULCMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDULCMessageFilter.java @@ -2,34 +2,34 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; -import io.github.dsheirer.module.decode.p25.message.tdu.lc.TDULinkControlMessage; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.message.tdu.TDULinkControlMessage; import java.util.Collections; import java.util.List; -public class TDULCMessageFilter extends Filter +public class TDULCMessageFilter extends Filter { - public TDULCMessageFilter() - { - super( "TDU Terminator Data Unit with Link Control" ); - } - - @Override - public boolean passes( Message message ) + public TDULCMessageFilter() { - return mEnabled && canProcess( message ); + super("TDU Terminator Data Unit with Link Control"); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof TDULinkControlMessage; + return mEnabled && canProcess(message); } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof TDULinkControlMessage; + } + + @Override public List> getFilterElements() { - return Collections.EMPTY_LIST; + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDUMessageFilter.java index ef50e572..97dfa4c0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TDUMessageFilter.java @@ -2,34 +2,34 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.message.tdu.TDUMessage; import java.util.Collections; import java.util.List; -public class TDUMessageFilter extends Filter +public class TDUMessageFilter extends Filter { - public TDUMessageFilter() - { - super( "TDU Terminator Data Unit" ); - } - - @Override - public boolean passes( Message message ) + public TDUMessageFilter() { - return mEnabled && canProcess( message ); + super("TDU Terminator Data Unit"); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof TDUMessage; + return mEnabled && canProcess(message); } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof TDUMessage; + } + + @Override public List> getFilterElements() { - return Collections.EMPTY_LIST; + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TSBKMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TSBKMessageFilterSet.java index 34e3f0e4..e96abe9e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TSBKMessageFilterSet.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/TSBKMessageFilterSet.java @@ -3,28 +3,24 @@ package io.github.dsheirer.module.decode.p25.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.MotorolaOpcode; -import io.github.dsheirer.module.decode.p25.message.tsbk.vendor.VendorOpcode; -import io.github.dsheirer.module.decode.p25.reference.Opcode; import io.github.dsheirer.module.decode.p25.reference.Vendor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -public class TSBKMessageFilterSet extends FilterSet +public class TSBKMessageFilterSet extends FilterSet { - public TSBKMessageFilterSet() - { - super( "TSBK Trunking Signalling Block" ); - - addFilter( new StandardOpcodeFilter() ); - addFilter( new MotorolaOpcodeFilter() ); - addFilter( new VendorOpcodeFilter() ); - } - + public TSBKMessageFilterSet() + { + super("TSBK Trunking Signalling Block"); + + addFilter(new StandardOpcodeFilter()); + } + // @Override // public boolean passes( Message message ) // { @@ -45,156 +41,56 @@ public class TSBKMessageFilterSet extends FilterSet // return false; // } - @Override - public boolean canProcess( Message message ) + @Override + public boolean canProcess(IMessage message) { - return message instanceof TSBKMessage; + return message instanceof TSBKMessage; } - public class StandardOpcodeFilter extends Filter - { - private HashMap> mStandardElements = - new HashMap>(); - - public StandardOpcodeFilter() - { - super( "Standard Opcode Filter" ); - - for( Opcode opcode: Opcode.values() ) - { - if( opcode != Opcode.UNKNOWN ) - { - mStandardElements.put( opcode, - new FilterElement( opcode ) ); - } - } - } - - @Override - public boolean passes( Message message ) - { - if( mEnabled && canProcess( message ) ) - { - Opcode opcode = ((TSBKMessage)message).getOpcode(); + public class StandardOpcodeFilter extends Filter + { + private HashMap> mStandardElements = + new HashMap>(); - if( mStandardElements.containsKey( opcode ) ) - { - return mStandardElements.get( opcode ).isEnabled(); - } - } - - return false; + public StandardOpcodeFilter() + { + super("Standard Opcode Filter"); + + for(Opcode opcode : Opcode.values()) + { + if(opcode != Opcode.OSP_UNKNOWN) + { + mStandardElements.put(opcode, new FilterElement(opcode)); + } + } } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - - return ((TSBKMessage)message).getVendor() == Vendor.STANDARD; + if(mEnabled && canProcess(message)) + { + Opcode opcode = ((TSBKMessage) message).getOpcode(); + + if(mStandardElements.containsKey(opcode)) + { + return mStandardElements.get(opcode).isEnabled(); + } + } + + return false; } - @Override + @Override + public boolean canProcess(IMessage message) + { + return ((TSBKMessage) message).getVendor() == Vendor.STANDARD; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mStandardElements.values() ); + return new ArrayList>(mStandardElements.values()); } - } - - public class MotorolaOpcodeFilter extends Filter - { - private HashMap> mMotorolaElements = - new HashMap>(); - - public MotorolaOpcodeFilter() - { - super( "Motorola Opcode Filter" ); - - for( MotorolaOpcode opcode: MotorolaOpcode.values() ) - { - if( opcode != MotorolaOpcode.UNKNOWN ) - { - mMotorolaElements.put( opcode, - new FilterElement( opcode ) ); - } - } - } - - @Override - public boolean passes( Message message ) - { - if( mEnabled && canProcess( message ) ) - { - MotorolaOpcode opcode = ((TSBKMessage)message).getMotorolaOpcode(); - - if( mMotorolaElements.containsKey( opcode ) ) - { - return mMotorolaElements.get( opcode ).isEnabled(); - } - } - - return false; - } - - @Override - public boolean canProcess( Message message ) - { - return ((TSBKMessage)message).getVendor() == Vendor.MOTOROLA; - } - - @Override - public List> getFilterElements() - { - return new ArrayList>( mMotorolaElements.values() ); - } - } - - public class VendorOpcodeFilter extends Filter - { - private HashMap> mVendorElements = - new HashMap>(); - - public VendorOpcodeFilter() - { - super( "Vendor Opcode Filter" ); - - for( VendorOpcode opcode: VendorOpcode.values() ) - { - if( opcode != VendorOpcode.UNKNOWN ) - { - mVendorElements.put( opcode, - new FilterElement( opcode ) ); - } - } - } - - @Override - public boolean passes( Message message ) - { - if( mEnabled && canProcess( message ) ) - { - MotorolaOpcode opcode = ((TSBKMessage)message).getMotorolaOpcode(); - - if( mVendorElements.containsKey( opcode ) ) - { - return mVendorElements.get( opcode ).isEnabled(); - } - } - - return false; - } - - @Override - public boolean canProcess( Message message ) - { - Vendor vendor = ((TSBKMessage)message).getVendor(); - - return vendor != Vendor.STANDARD && vendor != Vendor.MOTOROLA; - } - - @Override - public List> getFilterElements() - { - return new ArrayList>( mVendorElements.values() ); - } - } + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/UMBTCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/UMBTCMessageFilter.java new file mode 100644 index 00000000..a8c923ea --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/filter/UMBTCMessageFilter.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.UMBTCMessage; + +import java.util.Collections; +import java.util.List; + +public class UMBTCMessageFilter extends Filter +{ + public UMBTCMessageFilter() + { + super("Unconfirmed Multi-Block Trunking Control"); + } + + @Override + public boolean passes(IMessage message) + { + return mEnabled && canProcess(message); + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof UMBTCMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HDUMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HDUMessage.java index a72894f9..016fa344 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HDUMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HDUMessage.java @@ -1,271 +1,214 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ package io.github.dsheirer.module.decode.p25.message.hdu; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.Golay18; import io.github.dsheirer.edac.ReedSolomon_63_47_17; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.p25.message.P25Message; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; + public class HDUMessage extends P25Message { - private final static Logger mLog = LoggerFactory.getLogger( HDUMessage.class ); + private final static Logger mLog = LoggerFactory.getLogger(HDUMessage.class); - public static final int[] MESSAGE_INDICATOR_A = { 64,65,66,67,68,69,82,83, - 84,85,86,87,100,101,102,103,104,105,118,119,120,121,122,123,136,137,138, - 139,140,141,154,155,156,157,158,159 }; - public static final int[] MESSAGE_INDICATOR_B = { 172,173,174,175,176,177, - 190,191,192,193,194,195,208,209,210,211,212,213,226,227,228,229,230,231, - 244,245,246,247,248,249,262,263,264,265,266,267 }; - public static final int[] VENDOR_ID = { 280,281,282,283,284,285,298,299 }; - public static final int[] ALGORITHM_ID = { 300,301,302,303,316,317,318,319 }; - public static final int[] KEY_ID = { 320,321,334,335,336,337,338,339,352, - 353,354,355,356,357,370,371 }; - public static final int[] TALKGROUP_ID = { 372,373,374,375,388,389,390,391, - 392,393,406,407,408,409,410,411 }; + private static final int[] GOLAY_WORD_STARTS = {0, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198, 216, 234, 252, + 270, 288, 306, 324, 342, 360, 278, 396, 414, 432, 450, 468, 486, 504, 522, 540, 558, 576, 594, 612, 630}; - public static final int[] GOLAY_WORD_STARTS = { 64,82,100,118,136,154,172, - 190,208,226,244,262,280,298,316,334,352,370,388,406,424,442,460,478, - 496,514,532,550,568,586,604,622,640,658,676,694 }; + private static final int[] CW_HEX_0 = {0, 1, 2, 3, 4, 5, 6}; + private static final int[] CW_HEX_1 = {18, 19, 20, 21, 22, 23}; + private static final int[] CW_HEX_2 = {36, 37, 38, 39, 40, 41}; + private static final int[] CW_HEX_3 = {54, 55, 56, 57, 58, 59}; + private static final int[] CW_HEX_4 = {72, 73, 74, 75, 76, 77}; + private static final int[] CW_HEX_5 = {90, 91, 92, 93, 94, 95}; + private static final int[] CW_HEX_6 = {108, 109, 110, 111, 112, 113}; + private static final int[] CW_HEX_7 = {126, 127, 128, 129, 130, 131}; + private static final int[] CW_HEX_8 = {144, 145, 146, 147, 148, 149}; + private static final int[] CW_HEX_9 = {162, 163, 164, 165, 166, 167}; + private static final int[] CW_HEX_10 = {180, 181, 182, 183, 184, 185}; + private static final int[] CW_HEX_11 = {198, 199, 200, 201, 202, 203}; + private static final int[] CW_HEX_12 = {216, 217, 218, 219, 220, 221}; + private static final int[] CW_HEX_13 = {234, 235, 236, 237, 238, 239}; + private static final int[] CW_HEX_14 = {252, 253, 254, 255, 256, 257}; + private static final int[] CW_HEX_15 = {270, 271, 272, 273, 274, 275}; + private static final int[] CW_HEX_16 = {288, 289, 290, 291, 292, 293}; + private static final int[] CW_HEX_17 = {306, 307, 308, 309, 310, 311}; + private static final int[] CW_HEX_18 = {324, 325, 326, 327, 328, 329}; + private static final int[] CW_HEX_19 = {342, 343, 344, 345, 346, 347}; + private static final int[] RS_HEX_0 = {360, 361, 362, 363, 364, 365}; + private static final int[] RS_HEX_1 = {378, 379, 380, 381, 382, 383}; + private static final int[] RS_HEX_2 = {396, 397, 398, 399, 400, 401}; + private static final int[] RS_HEX_3 = {414, 415, 416, 417, 418, 419}; + private static final int[] RS_HEX_4 = {432, 433, 434, 435, 436, 437}; + private static final int[] RS_HEX_5 = {450, 451, 452, 453, 454, 455}; + private static final int[] RS_HEX_6 = {468, 469, 470, 471, 472, 473}; + private static final int[] RS_HEX_7 = {486, 487, 488, 489, 490, 491}; + private static final int[] RS_HEX_8 = {504, 505, 506, 507, 508, 509}; + private static final int[] RS_HEX_9 = {522, 523, 524, 525, 526, 527}; + private static final int[] RS_HEX_10 = {540, 541, 542, 543, 544, 545}; + private static final int[] RS_HEX_11 = {558, 559, 560, 561, 562, 563}; + private static final int[] RS_HEX_12 = {576, 577, 578, 579, 580, 581}; + private static final int[] RS_HEX_13 = {594, 595, 596, 597, 598, 599}; + private static final int[] RS_HEX_14 = {612, 613, 614, 615, 616, 617}; + private static final int[] RS_HEX_15 = {630, 631, 632, 633, 634, 635}; - public static final int[] CW_HEX_0 = { 64,65,66,67,68,69 }; - public static final int[] CW_HEX_1 = { 82,83,84,85,86,87 }; - public static final int[] CW_HEX_2 = { 100,101,102,103,104,105 }; - public static final int[] CW_HEX_3 = { 118,119,120,121,122,123 }; - public static final int[] CW_HEX_4 = { 136,137,138,139,140,141 }; - public static final int[] CW_HEX_5 = { 154,155,156,157,158,159 }; - public static final int[] CW_HEX_6 = { 172,173,174,175,176,177 }; - public static final int[] CW_HEX_7 = { 190,191,192,193,194,195 }; - public static final int[] CW_HEX_8 = { 208,209,210,211,212,213 }; - public static final int[] CW_HEX_9 = { 226,227,228,229,230,231 }; - public static final int[] CW_HEX_10 = { 244,245,246,247,248,249 }; - public static final int[] CW_HEX_11 = { 262,263,264,265,266,267 }; - public static final int[] CW_HEX_12 = { 280,281,282,283,284,285 }; - public static final int[] CW_HEX_13 = { 298,299,300,301,302,303 }; - public static final int[] CW_HEX_14 = { 316,317,318,319,320,321 }; - public static final int[] CW_HEX_15 = { 334,335,336,337,338,339 }; - public static final int[] CW_HEX_16 = { 352,353,354,355,356,357 }; - public static final int[] CW_HEX_17 = { 370,371,372,373,374,375 }; - public static final int[] CW_HEX_18 = { 388,389,390,391,392,393 }; - public static final int[] CW_HEX_19 = { 406,407,408,409,410,411 }; - public static final int[] RS_HEX_0 = { 424,425,426,427,428,429 }; - public static final int[] RS_HEX_1 = { 442,443,444,445,446,447 }; - public static final int[] RS_HEX_2 = { 460,461,462,463,464,465 }; - public static final int[] RS_HEX_3 = { 478,479,480,481,482,483 }; - public static final int[] RS_HEX_4 = { 496,497,498,499,500,501 }; - public static final int[] RS_HEX_5 = { 514,515,516,517,518,519 }; - public static final int[] RS_HEX_6 = { 532,533,534,535,536,537 }; - public static final int[] RS_HEX_7 = { 550,551,552,553,554,555 }; - public static final int[] RS_HEX_8 = { 568,569,570,571,572,573 }; - public static final int[] RS_HEX_9 = { 586,587,588,589,590,591 }; - public static final int[] RS_HEX_10 = { 604,605,606,607,608,609 }; - public static final int[] RS_HEX_11 = { 622,623,624,625,626,627 }; - public static final int[] RS_HEX_12 = { 640,641,642,643,644,645 }; - public static final int[] RS_HEX_13 = { 658,659,660,661,662,663 }; - public static final int[] RS_HEX_14 = { 676,677,678,679,680,681 }; - public static final int[] RS_HEX_15 = { 694,695,696,697,698,699 }; - - /* Reed-Solomon(36,20,17) code protects the header word. Maximum - * correctable errors are: Hamming Distance(17) / 2 = 8 */ - public static final ReedSolomon_63_47_17 mReedSolomonDecoder = - new ReedSolomon_63_47_17( 8 ); - - public HDUMessage( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) + private HeaderData mHeaderData; + + public HDUMessage(CorrectedBinaryMessage message, int nac, long timestamp) { - super( message, duid, aliasList ); - - /* NID CRC is checked in the message framer, thus a constructed message - * means it passed the CRC */ - mCRC = new CRC[ 3 ]; - mCRC[ 0 ] = CRC.PASSED; - - checkCRC(); + super(message, nac, timestamp); } - - private void checkCRC() - { - /* Golay( 18,6,18 ) error detection and correction */ - for( int index: GOLAY_WORD_STARTS ) - { - Golay18.checkAndCorrect( mMessage, index ); - } - - mCRC[ 1 ] = CRC.PASSED; - /* Reed-Solomon( 36,20,17 ) error detection and correction - * Check the Reed-Solomon parity bits. The RS decoder expects the link - * control data and reed solomon parity hex codewords in reverse order. - * The RS(24,12,13) code used by P25 removes the left-hand 47 data hex - * words, so we replace them with zeros. */ - int[] input = new int [63]; - int[] output = new int [63]; - - input[ 0 ] = mMessage.getInt( RS_HEX_15 ); - input[ 1 ] = mMessage.getInt( RS_HEX_14 ); - input[ 2 ] = mMessage.getInt( RS_HEX_13 ); - input[ 3 ] = mMessage.getInt( RS_HEX_12 ); - input[ 4 ] = mMessage.getInt( RS_HEX_11 ); - input[ 5 ] = mMessage.getInt( RS_HEX_10 ); - input[ 6 ] = mMessage.getInt( RS_HEX_9 ); - input[ 7 ] = mMessage.getInt( RS_HEX_8 ); - input[ 8 ] = mMessage.getInt( RS_HEX_7 ); - input[ 9 ] = mMessage.getInt( RS_HEX_6 ); - input[ 10 ] = mMessage.getInt( RS_HEX_5 ); - input[ 11 ] = mMessage.getInt( RS_HEX_4 ); - input[ 12 ] = mMessage.getInt( RS_HEX_3 ); - input[ 13 ] = mMessage.getInt( RS_HEX_2 ); - input[ 14 ] = mMessage.getInt( RS_HEX_1 ); - input[ 15 ] = mMessage.getInt( RS_HEX_0 ); - - input[ 16 ] = mMessage.getInt( CW_HEX_19 ); - input[ 17 ] = mMessage.getInt( CW_HEX_18 ); - input[ 18 ] = mMessage.getInt( CW_HEX_17 ); - input[ 19 ] = mMessage.getInt( CW_HEX_16 ); - input[ 20 ] = mMessage.getInt( CW_HEX_15 ); - input[ 21 ] = mMessage.getInt( CW_HEX_14 ); - input[ 22 ] = mMessage.getInt( CW_HEX_13 ); - input[ 23 ] = mMessage.getInt( CW_HEX_12 ); - input[ 24 ] = mMessage.getInt( CW_HEX_11 ); - input[ 25 ] = mMessage.getInt( CW_HEX_10 ); - input[ 26 ] = mMessage.getInt( CW_HEX_9 ); - input[ 27 ] = mMessage.getInt( CW_HEX_8 ); - input[ 28 ] = mMessage.getInt( CW_HEX_7 ); - input[ 29 ] = mMessage.getInt( CW_HEX_6 ); - input[ 30 ] = mMessage.getInt( CW_HEX_5 ); - input[ 31 ] = mMessage.getInt( CW_HEX_4 ); - input[ 32 ] = mMessage.getInt( CW_HEX_3 ); - input[ 33 ] = mMessage.getInt( CW_HEX_2 ); - input[ 34 ] = mMessage.getInt( CW_HEX_1 ); - input[ 35 ] = mMessage.getInt( CW_HEX_0 ); + @Override + public DataUnitID getDUID() + { + return DataUnitID.HEADER_DATA_UNIT; + } + + public HeaderData getHeaderData() + { + if(mHeaderData == null) + { + extractHeaderData(); + } + + return mHeaderData; + } + + /** + * Performs error detection and correction and extracts the header data from the HDU message + */ + private void extractHeaderData() + { + for(int index : GOLAY_WORD_STARTS) + { + Golay18.checkAndCorrect(getMessage(), index); + } + + /* Reed-Solomon( 36,20,17 ) error detection and correction + * Check the Reed-Solomon parity bits. The RS decoder expects the link + * control data and reed solomon parity hex codewords in reverse order. + * The RS(24,12,13) code used by P25 removes the left-hand 47 data hex + * words, so we replace them with zeros. */ + int[] input = new int[63]; + int[] output = new int[63]; + + input[0] = getMessage().getInt(RS_HEX_15); + input[1] = getMessage().getInt(RS_HEX_14); + input[2] = getMessage().getInt(RS_HEX_13); + input[3] = getMessage().getInt(RS_HEX_12); + input[4] = getMessage().getInt(RS_HEX_11); + input[5] = getMessage().getInt(RS_HEX_10); + input[6] = getMessage().getInt(RS_HEX_9); + input[7] = getMessage().getInt(RS_HEX_8); + input[8] = getMessage().getInt(RS_HEX_7); + input[9] = getMessage().getInt(RS_HEX_6); + input[10] = getMessage().getInt(RS_HEX_5); + input[11] = getMessage().getInt(RS_HEX_4); + input[12] = getMessage().getInt(RS_HEX_3); + input[13] = getMessage().getInt(RS_HEX_2); + input[14] = getMessage().getInt(RS_HEX_1); + input[15] = getMessage().getInt(RS_HEX_0); + + input[16] = getMessage().getInt(CW_HEX_19); + input[17] = getMessage().getInt(CW_HEX_18); + input[18] = getMessage().getInt(CW_HEX_17); + input[19] = getMessage().getInt(CW_HEX_16); + input[20] = getMessage().getInt(CW_HEX_15); + input[21] = getMessage().getInt(CW_HEX_14); + input[22] = getMessage().getInt(CW_HEX_13); + input[23] = getMessage().getInt(CW_HEX_12); + input[24] = getMessage().getInt(CW_HEX_11); + input[25] = getMessage().getInt(CW_HEX_10); + input[26] = getMessage().getInt(CW_HEX_9); + input[27] = getMessage().getInt(CW_HEX_8); + input[28] = getMessage().getInt(CW_HEX_7); + input[29] = getMessage().getInt(CW_HEX_6); + input[30] = getMessage().getInt(CW_HEX_5); + input[31] = getMessage().getInt(CW_HEX_4); + input[32] = getMessage().getInt(CW_HEX_3); + input[33] = getMessage().getInt(CW_HEX_2); + input[34] = getMessage().getInt(CW_HEX_1); + input[35] = getMessage().getInt(CW_HEX_0); /* indexes 36 - 62 are defaulted to zero */ - boolean irrecoverableErrors = mReedSolomonDecoder.decode( input, output ); + //Reed-Solomon(36,20,17) code protects the header word. Maximum correctable errors are: 8 + ReedSolomon_63_47_17 reedSolomon_63_47_17 = new ReedSolomon_63_47_17(8); - if( irrecoverableErrors ) + boolean irrecoverableErrors; + + try { - mCRC[ 2 ] = CRC.FAILED_CRC; + irrecoverableErrors = reedSolomon_63_47_17.decode(input, output); + } + catch(Exception e) + { + irrecoverableErrors = true; + } + + BinaryMessage binaryMessage = new BinaryMessage(120); + + int pointer = 0; + + for(int x = 35; x >= 16; x--) + { + if(output[x] != -1) + { + binaryMessage.load(pointer, 6, output[x]); + } + + pointer += 6; + } + + mHeaderData = new HeaderData(binaryMessage); + + if(irrecoverableErrors) + { + mHeaderData.setValid(false); } else { - mCRC[ 2 ] = CRC.PASSED; - - /* Only fix the codeword data hex codewords */ - repairHexCodeword( input, output, 16, CW_HEX_19 ); - repairHexCodeword( input, output, 17, CW_HEX_18 ); - repairHexCodeword( input, output, 18, CW_HEX_17 ); - repairHexCodeword( input, output, 19, CW_HEX_16 ); - repairHexCodeword( input, output, 20, CW_HEX_15 ); - repairHexCodeword( input, output, 21, CW_HEX_14 ); - repairHexCodeword( input, output, 22, CW_HEX_13 ); - repairHexCodeword( input, output, 23, CW_HEX_12 ); - repairHexCodeword( input, output, 24, CW_HEX_11 ); - repairHexCodeword( input, output, 25, CW_HEX_10 ); - repairHexCodeword( input, output, 26, CW_HEX_9 ); - repairHexCodeword( input, output, 27, CW_HEX_8 ); - repairHexCodeword( input, output, 28, CW_HEX_7 ); - repairHexCodeword( input, output, 29, CW_HEX_6 ); - repairHexCodeword( input, output, 30, CW_HEX_5 ); - repairHexCodeword( input, output, 31, CW_HEX_4 ); - repairHexCodeword( input, output, 32, CW_HEX_3 ); - repairHexCodeword( input, output, 33, CW_HEX_2 ); - repairHexCodeword( input, output, 34, CW_HEX_1 ); - repairHexCodeword( input, output, 35, CW_HEX_0 ); + //If we corrected any bit errors, update the original message with the bit error count + for(int x = 0; x < 23; x++) + { + if(output[x] != input[x]) + { + getMessage().incrementCorrectedBitCount(Integer.bitCount((output[x] ^ input[x]))); + } + } } - } - - private void repairHexCodeword( int[] input, int[] output, int index, int[] indexSet ) - { - if( input[ index ] != output[ index ] ) - { - mMessage.load( indexSet[ 0 ], 6, output[ index ] ); - mCRC[ 2 ] = CRC.CORRECTED; - } - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - - sb.append( " TALKGROUP:" + getTalkgroupID() ); - - Encryption encryption = getEncryption(); - - if( encryption != Encryption.UNENCRYPTED ) - { - sb.append( " ENCRYPTION:" ); - - if( encryption == Encryption.UNKNOWN ) - { - sb.append( "UNK ALGO ID:" ); - sb.append( mMessage.getInt( ALGORITHM_ID ) ); - } - else - { - sb.append( encryption.name() ); - } - sb.append( " KEY ID:" + getKeyID() ); - sb.append( " MSG INDICATOR:" + getMessageIndicator() ); - } - else - { - sb.append( " UNENCRYPTED" ); - } - - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); } - - public String getMessageIndicator() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getHex( MESSAGE_INDICATOR_A, 9 ) ); - sb.append( mMessage.getHex( MESSAGE_INDICATOR_B, 9 ) ); - return sb.toString(); - } - - public Encryption getEncryption() - { - return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); - } - - public boolean isEncryptedAudio() - { - return getEncryption() != Encryption.UNENCRYPTED; - } - - public int getKeyID() - { - return mMessage.getInt( KEY_ID ); - } - - public String getTalkgroupID() - { - return mMessage.getHex( TALKGROUP_ID, 4 ); - } - - @Override - public String getToID() - { - return getTalkgroupID(); - } - - public boolean isValid() - { - return mCRC[ 2 ] != null && mCRC[ 2 ] != CRC.FAILED_CRC; - } + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NAC:").append(getNAC()); + sb.append(" HDU ").append(getHeaderData()); + return sb.toString(); + } + + public List getIdentifiers() + { + return getHeaderData().getIdentifiers(); + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HeaderData.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HeaderData.java new file mode 100644 index 00000000..a3e7c399 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/hdu/HeaderData.java @@ -0,0 +1,175 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.hdu; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.reference.Encryption; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Header Data Unit (HDU) Information + */ +public class HeaderData +{ + private static final int[] MESSAGE_INDICATOR_A = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + private static final int[] MESSAGE_INDICATOR_B = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] VENDOR_ID = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] ALGORITHM_ID = {80, 81, 82, 83, 84, 85, 86, 87}; + private static final int[] KEY_ID = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + private static final int[] TALKGROUP_ID = {104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; + + private boolean mValid = true; + private BinaryMessage mMessage; + private Identifier mEncryptionKey; + private Identifier mTalkgroup; + private List mIdentifiers; + + public HeaderData(BinaryMessage message) + { + mMessage = message; + } + + public BinaryMessage getMessage() + { + return mMessage; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(isValid()) + { + sb.append("TALKGROUP:" + getTalkgroup()); + + Vendor vendor = getVendor(); + + if(vendor != Vendor.STANDARD) + { + sb.append(" VENDOR:").append(vendor.getLabel()); + } + + if(isEncryptedAudio()) + { + sb.append(" ENCRYPTION:").append(getEncryptionKey()); + sb.append(" MI:").append(getMessageIndicator()); + } + else + { + sb.append(" UNENCRYPTED"); + } + } + else + { + sb.append(" **CRC FAILED**"); + } + + return sb.toString(); + } + + /** + * Indicates if this message is valid, meaning that it has passed the CRC error detection and + * correction check. + */ + public boolean isValid() + { + return mValid; + } + + /** + * Sets the valid flag for this message set + */ + public void setValid(boolean valid) + { + mValid = valid; + } + + public String getMessageIndicator() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessage().getHex(MESSAGE_INDICATOR_A, 9)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_B, 9)); + + return sb.toString(); + } + + public Vendor getVendor() + { + return Vendor.fromValue(getMessage().getInt(VENDOR_ID)); + } + + public Identifier getEncryptionKey() + { + if(mEncryptionKey == null && isEncryptedAudio()) + { + mEncryptionKey = APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID), + getMessage().getInt(KEY_ID)); + } + + return mEncryptionKey; + } + + public Encryption getEncryption() + { + return Encryption.fromValue(getMessage().getInt(ALGORITHM_ID)); + } + + public boolean isEncryptedAudio() + { + return getEncryption() != Encryption.UNENCRYPTED; + } + + public Identifier getTalkgroup() + { + if(mTalkgroup == null) + { + mTalkgroup = APCO25ToTalkgroup.createGroup(getMessage().getInt(TALKGROUP_ID)); + } + + return mTalkgroup; + } + + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(isValid()) + { + mIdentifiers.add(getTalkgroup()); + mIdentifiers.add(getEncryptionKey()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlOpcode.java new file mode 100644 index 00000000..a7c6e1a8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlOpcode.java @@ -0,0 +1,155 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc; + +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +public enum LinkControlOpcode +{ + GROUP_VOICE_CHANNEL_USER("GROUP VOICE CHANNEL USER", 0), + RESERVED_01("RESERVED-01", 1), + GROUP_VOICE_CHANNEL_UPDATE("GROUP VOICE CHANNEL UPDATE", 2), + UNIT_TO_UNIT_VOICE_CHANNEL_USER("UNIT-TO-UNIT VOICE CHANNEL USER", 3), + GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT("GROUP VOICE CHANNEL UPDATE EXPLICIT", 4), + UNIT_TO_UNIT_ANSWER_REQUEST("UNIT-TO-UNIT ANSWER REQUEST", 5), + TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER("TELEPHONE INTERCONNECT VOICE CHANNEL USER", 6), + TELEPHONE_INTERCONNECT_ANSWER_REQUEST("TELEPHONE INTERCONNECT ANSWER REQUEST", 7), + RESERVED_08("RESERVED-08", 8), + RESERVED_09("RESERVED-09", 9), + RESERVED_0A("RESERVED-0A", 10), + RESERVED_0B("RESERVED-0B", 11), + RESERVED_0C("RESERVED-0C", 12), + RESERVED_0D("RESERVED-0D", 13), + RESERVED_0E("RESERVED-0E", 14), + CALL_TERMINATION_OR_CANCELLATION("CALL TERMINATION", 15), + GROUP_AFFILIATION_QUERY("GROUP AFFILIATION QUERY", 16), + UNIT_REGISTRATION_COMMAND("UNIT REGISTRATION COMMAND", 17), + UNIT_AUTHENTICATION_COMMAND("UNIT AUTHENTICATION COMMAND", 18), + STATUS_QUERY("STATUS QUERY", 19), + STATUS_UPDATE("STATUS_UPDATE", 20), + MESSAGE_UPDATE("MESSAGE UPDATE", 21), + CALL_ALERT("CALL ALERT", 22), + EXTENDED_FUNCTION_COMMAND("EXTENDED FUNCTION COMMAND", 23), + CHANNEL_IDENTIFIER_UPDATE("CHANNEL IDENTIFIER UPDATE", 24), + CHANNEL_IDENTIFIER_UPDATE_EXPLICIT("CHANNEL IDENTIFIER UPDATE EXPLICIT", 25), + RESERVED_1A("RESERVED-1A", 26), + RESERVED_1B("RESERVED-1B", 27), + RESERVED_1C("RESERVED-1C", 28), + RESERVED_1D("RESERVED-1D", 29), + RESERVED_1E("RESERVED-1E", 30), + RESERVED_1F("RESERVED-1F", 31), + SYSTEM_SERVICE_BROADCAST("SYSTEM SERVICE BROADCAST", 32), + SECONDARY_CONTROL_CHANNEL_BROADCAST("SECONDARY CONTROL CHANNEL BROADCAST", 33), + ADJACENT_SITE_STATUS_BROADCAST("ADJACENT SITE STATUS", 34), + RFSS_STATUS_BROADCAST("RFSS STATUS BROADCAST", 35), + NETWORK_STATUS_BROADCAST("NET STATUS BROADCAST", 36), + PROTECTION_PARAMETER_BROADCAST("ENCRYPTION PARAMETERS BROADCAST", 37), + SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT("SECONDARY CONTROL CHANNEL BROADCAST EXPLICIT", 38), + ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT("ADJACENT SITE STATUS EXPLICIT", 39), + RFSS_STATUS_BROADCAST_EXPLICIT("RFSS STATUS BROADCAST EXPLICIT", 40), + NETWORK_STATUS_BROADCAST_EXPLICIT("NETWORK STATUS BROADCAST EXPLICIT", 41), + RESERVED_2A("RESERVED-2A", 42), + RESERVED_2B("RESERVED-2B", 43), + RESERVED_2C("RESERVED-2C", 44), + RESERVED_2D("RESERVED-2D", 45), + RESERVED_2E("RESERVED-2E", 46), + RESERVED_2F("RESERVED-2F", 47), + RESERVED_30("RESERVED-30", 48), + RESERVED_31("RESERVED-31", 49), + RESERVED_32("RESERVED-32", 50), + RESERVED_33("RESERVED-33", 51), + RESERVED_34("RESERVED-34", 52), + RESERVED_35("RESERVED-35", 53), + RESERVED_36("RESERVED-36", 54), + RESERVED_37("RESERVED-37", 55), + RESERVED_38("RESERVED-38", 56), + RESERVED_39("RESERVED-39", 57), + RESERVED_3A("RESERVED-3A", 58), + RESERVED_3B("RESERVED-3B", 59), + RESERVED_3C("RESERVED-3C", 60), + RESERVED_3D("RESERVED-3D", 61), + RESERVED_3E("RESERVED-3E", 62), + RESERVED_3F("RESERVED-3F", 63), + + MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER("PATCH GROUP VOICE CHANNEL USER", 0), + MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_UPDATE("PATCH GROUP VOICE CHANNEL UPDATE", 1), + MOTOROLA_PATCH_GROUP_ADD("PATCH GROUP ADD", 3), + MOTOROLA_PATCH_GROUP_DELETE("PATCH GROUP DELETE", 4), + MOTOROLA_TALK_COMPLETE("TALK_COMPLETE", 15), + MOTOROLA_UNKNOWN("UNKNOWN", -1), + + UNKNOWN("UNKNOWN", -1); + + private String mLabel; + private int mCode; + + LinkControlOpcode(String label, int code) + { + mLabel = label; + mCode = code; + } + + public String getLabel() + { + return mLabel; + } + + public int getCode() + { + return mCode; + } + + public static LinkControlOpcode fromValue(int value, Vendor vendor) + { + switch(vendor) + { + case STANDARD: + if(0 <= value && value <= 63) + { + return values()[value]; + } + break; + case MOTOROLA: + switch(value) + { + case 0: + return MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER; + case 1: + return MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_UPDATE; + case 3: + return MOTOROLA_PATCH_GROUP_ADD; + case 4: + return MOTOROLA_PATCH_GROUP_DELETE; + case 15: + return MOTOROLA_TALK_COMPLETE; + default: + return MOTOROLA_UNKNOWN; + } + default: + if(0 <= value && value <= 63) + { + return values()[value]; + } + } + + return UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWord.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWord.java new file mode 100644 index 00000000..b1b75e34 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWord.java @@ -0,0 +1,164 @@ +package io.github.dsheirer.module.decode.p25.message.lc; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.List; + +/** + * APCO 25 Link Control Word. This message word is contained in Logical Link Data Unit 1 and Terminator with + * Link Control messages. + */ +public abstract class LinkControlWord +{ + private static final int ENCRYPTION_FLAG = 0; + private static final int STANDARD_VENDOR_ID_FLAG = 1; + private static final int[] OPCODE = {2, 3, 4, 5, 6, 7}; + private static final int[] VENDOR = {8, 9, 10, 11, 12, 13, 14, 15}; + + private BinaryMessage mMessage; + private LinkControlOpcode mLinkControlOpcode; + private boolean mValid = true; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LinkControlWord(BinaryMessage message) + { + mMessage = message; + } + + /** + * Binary message sequence for this LCW + */ + protected BinaryMessage getMessage() + { + return mMessage; + } + + /** + * Sets the valid flag for this message to mark a flag as invalid (false) or the default value is true. + */ + public void setValid(boolean valid) + { + mValid = valid; + } + + /** + * Indicates if this link control word is valid and has passed all error detection and correction routines. + */ + public boolean isValid() + { + return mValid; + } + + /** + * Indicates if this is an encrypted LCW + */ + public boolean isEncrypted() + { + return getMessage().get(ENCRYPTION_FLAG); + } + + /** + * Indicates if this is a standard vendor format LCW + */ + public boolean isStandardVendorFormat() + { + return isStandardVendorFormat(getMessage()); + } + + /** + * Indicates if the link control word message has standard vendor format. + */ + public static boolean isStandardVendorFormat(BinaryMessage binaryMessage) + { + return binaryMessage.get(STANDARD_VENDOR_ID_FLAG); + } + + /** + * Vendor format for this link control word. + */ + public Vendor getVendor() + { + return getVendor(getMessage()); + } + + /** + * Lookup the Vendor format for the specified LCW + */ + public static Vendor getVendor(BinaryMessage binaryMessage) + { + if(isStandardVendorFormat(binaryMessage)) + { + return Vendor.STANDARD; + } + else + { + return Vendor.fromValue(binaryMessage.getInt(VENDOR)); + } + } + + /** + * Opcode for this LCW + */ + public LinkControlOpcode getOpcode() + { + if(mLinkControlOpcode == null) + { + mLinkControlOpcode = getOpcode(getMessage()); + } + + return mLinkControlOpcode; + } + + /** + * Identifies the link control word opcode from the binary message. + */ + public static LinkControlOpcode getOpcode(BinaryMessage binaryMessage) + { + return LinkControlOpcode.fromValue(binaryMessage.getInt(OPCODE), getVendor(binaryMessage)); + } + + /** + * List of identifiers provided by the message + */ + public abstract List getIdentifiers(); + + /** + * Creates a string with the basic Link Control Word information + */ + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("***LINK CONTROL CRC FAIL*** "); + } + + sb.append(getOpcode().getLabel()); + + if(isEncrypted()) + { + sb.append(" ENCRYPTED"); + } + else + { + if(!isStandardVendorFormat()) + { + Vendor vendor = getVendor(); + + if(vendor != Vendor.STANDARD) + { + sb.append(" ").append(vendor.getLabel()); + } + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWordFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWordFactory.java new file mode 100644 index 00000000..0773ee1d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/LinkControlWordFactory.java @@ -0,0 +1,148 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.p25.message.lc.motorola.LCMotorolaPatchGroupAdd; +import io.github.dsheirer.module.decode.p25.message.lc.motorola.LCMotorolaPatchGroupDelete; +import io.github.dsheirer.module.decode.p25.message.lc.motorola.LCMotorolaPatchGroupVoiceChannelUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.motorola.LCMotorolaPatchGroupVoiceChannelUser; +import io.github.dsheirer.module.decode.p25.message.lc.motorola.LCMotorolaTalkComplete; +import io.github.dsheirer.module.decode.p25.message.lc.motorola.LCMotorolaUnknownOpcode; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCAdjacentSiteStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCAdjacentSiteStatusBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCCallAlert; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCCallTermination; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCExtendedFunctionCommand; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCFrequencyBandUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCFrequencyBandUpdateExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCGroupAffiliationQuery; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCGroupVoiceChannelUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCGroupVoiceChannelUpdateExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCGroupVoiceChannelUser; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCMessageUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCNetworkStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCNetworkStatusBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCProtectionParameterBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCRFSSStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCRFSSStatusBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCSecondaryControlChannelBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCSecondaryControlChannelBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCStatusQuery; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCStatusUpdate; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCSystemServiceBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCTelephoneInterconnectAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCTelephoneInterconnectVoiceChannelUser; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCUnitAuthenticationCommand; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCUnitRegistrationCommand; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCUnitToUnitAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCUnitToUnitVoiceChannelUser; + +/** + * Factory class for creating link control word (LCW) message parsers. + */ +public class LinkControlWordFactory +{ + /** + * Creates a link control word from the binary message sequence. + * + * @param binaryMessage containing the LCW binary message sequence. + */ + public static LinkControlWord create(BinaryMessage binaryMessage) + { + LinkControlOpcode opcode = LinkControlWord.getOpcode(binaryMessage); + switch(opcode) + { + case ADJACENT_SITE_STATUS_BROADCAST: + return new LCAdjacentSiteStatusBroadcast(binaryMessage); + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + return new LCAdjacentSiteStatusBroadcastExplicit(binaryMessage); + case CALL_ALERT: + return new LCCallAlert(binaryMessage); + case CALL_TERMINATION_OR_CANCELLATION: + return new LCCallTermination(binaryMessage); + case CHANNEL_IDENTIFIER_UPDATE: + return new LCFrequencyBandUpdate(binaryMessage); + case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: + return new LCFrequencyBandUpdateExplicit(binaryMessage); + case EXTENDED_FUNCTION_COMMAND: + return new LCExtendedFunctionCommand(binaryMessage); + case GROUP_AFFILIATION_QUERY: + return new LCGroupAffiliationQuery(binaryMessage); + case GROUP_VOICE_CHANNEL_USER: + return new LCGroupVoiceChannelUser(binaryMessage); + case GROUP_VOICE_CHANNEL_UPDATE: + return new LCGroupVoiceChannelUpdate(binaryMessage); + case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: + return new LCGroupVoiceChannelUpdateExplicit(binaryMessage); + case MESSAGE_UPDATE: + return new LCMessageUpdate(binaryMessage); + case NETWORK_STATUS_BROADCAST: + return new LCNetworkStatusBroadcast(binaryMessage); + case NETWORK_STATUS_BROADCAST_EXPLICIT: + return new LCNetworkStatusBroadcastExplicit(binaryMessage); + case PROTECTION_PARAMETER_BROADCAST: + return new LCProtectionParameterBroadcast(binaryMessage); + case RFSS_STATUS_BROADCAST: + return new LCRFSSStatusBroadcast(binaryMessage); + case RFSS_STATUS_BROADCAST_EXPLICIT: + return new LCRFSSStatusBroadcastExplicit(binaryMessage); + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + return new LCSecondaryControlChannelBroadcast(binaryMessage); + case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + return new LCSecondaryControlChannelBroadcastExplicit(binaryMessage); + case STATUS_QUERY: + return new LCStatusQuery(binaryMessage); + case STATUS_UPDATE: + return new LCStatusUpdate(binaryMessage); + case SYSTEM_SERVICE_BROADCAST: + return new LCSystemServiceBroadcast(binaryMessage); + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + return new LCTelephoneInterconnectAnswerRequest(binaryMessage); + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: + return new LCTelephoneInterconnectVoiceChannelUser(binaryMessage); + case UNIT_AUTHENTICATION_COMMAND: + return new LCUnitAuthenticationCommand(binaryMessage); + case UNIT_REGISTRATION_COMMAND: + return new LCUnitRegistrationCommand(binaryMessage); + case UNIT_TO_UNIT_ANSWER_REQUEST: + return new LCUnitToUnitAnswerRequest(binaryMessage); + case UNIT_TO_UNIT_VOICE_CHANNEL_USER: + return new LCUnitToUnitVoiceChannelUser(binaryMessage); + + case MOTOROLA_PATCH_GROUP_ADD: + return new LCMotorolaPatchGroupAdd(binaryMessage); + case MOTOROLA_PATCH_GROUP_DELETE: + return new LCMotorolaPatchGroupDelete(binaryMessage); + case MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER: + return new LCMotorolaPatchGroupVoiceChannelUser(binaryMessage); + case MOTOROLA_TALK_COMPLETE: + return new LCMotorolaTalkComplete(binaryMessage); + case MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_UPDATE: + return new LCMotorolaPatchGroupVoiceChannelUpdate(binaryMessage); + case MOTOROLA_UNKNOWN: + return new LCMotorolaUnknownOpcode(binaryMessage); + + default: + return new UnknownLinkControlWord(binaryMessage); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/UnknownLinkControlWord.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/UnknownLinkControlWord.java new file mode 100644 index 00000000..27445e6d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/UnknownLinkControlWord.java @@ -0,0 +1,39 @@ +package io.github.dsheirer.module.decode.p25.message.lc; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown link control word. + */ +public class UnknownLinkControlWord extends LinkControlWord +{ + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public UnknownLinkControlWord(BinaryMessage message) + { + super(message); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" UNKNOWN/UNRECOGNIZED OPCODE"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupAdd.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupAdd.java new file mode 100644 index 00000000..13c36c12 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupAdd.java @@ -0,0 +1,144 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; + +import java.util.ArrayList; +import java.util.List; + +public class LCMotorolaPatchGroupAdd extends MotorolaLinkControlWord +{ + private static final int[] PATCH_GROUP = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] PATCHED_GROUP_1 = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] PATCHED_GROUP_2 = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private APCO25PatchGroup mPatchGroup; + private TalkgroupIdentifier mPatchedGroup1; + private TalkgroupIdentifier mPatchedGroup2; + private List mIdentifiers; + + public LCMotorolaPatchGroupAdd(BinaryMessage message) + { + super(message); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("MOTOROLA ADD PATCH GROUP:").append(getPatchGroup()); + sb.append(" MSG:").append(getMessage().toHexString()); + + return sb.toString(); + } + + /** + * Patch Group + */ + public Identifier getPatchGroup() + { + if(mPatchGroup == null) + { + PatchGroup patchGroup = new PatchGroup(APCO25AnyTalkgroup.create(getMessage().getInt(PATCH_GROUP))); + patchGroup.addPatchedGroups(getPatchedGroups()); + mPatchGroup = APCO25PatchGroup.create(patchGroup); + } + + return mPatchGroup; + } + + public List getPatchedGroups() + { + List patchedGroups = new ArrayList<>(); + + if(hasPatchedGroup1()) + { + patchedGroups.add(getPatchedGroup1()); + } + + if(hasPatchedGroup2()) + { + patchedGroups.add(getPatchedGroup2()); + } + + return patchedGroups; + } + + /** + * Patched Group 1 + */ + public TalkgroupIdentifier getPatchedGroup1() + { + if(mPatchedGroup1 == null) + { + mPatchedGroup1 = APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCHED_GROUP_1)); + } + + return mPatchedGroup1; + } + + public boolean hasPatchedGroup1() + { + return getMessage().getInt(PATCHED_GROUP_1) != 0 && + (getMessage().getInt(PATCH_GROUP) != getMessage().getInt(PATCHED_GROUP_1)); + } + + /** + * Patched Group 2 + */ + public TalkgroupIdentifier getPatchedGroup2() + { + if(mPatchedGroup2 == null) + { + mPatchedGroup2 = APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCHED_GROUP_2)); + } + + return mPatchedGroup2; + } + + public boolean hasPatchedGroup2() + { + return getMessage().getInt(PATCHED_GROUP_2) != 0 && + (getMessage().getInt(PATCH_GROUP) != getMessage().getInt(PATCHED_GROUP_2)); + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupDelete.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupDelete.java new file mode 100644 index 00000000..c4d1ca49 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupDelete.java @@ -0,0 +1,143 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; + +import java.util.ArrayList; +import java.util.List; + +public class LCMotorolaPatchGroupDelete extends MotorolaLinkControlWord +{ + private static final int[] PATCH_GROUP = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] PATCHED_GROUP_1 = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] PATCHED_GROUP_2 = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private APCO25PatchGroup mPatchGroup; + private TalkgroupIdentifier mPatchedGroup1; + private TalkgroupIdentifier mPatchedGroup2; + private List mIdentifiers; + + public LCMotorolaPatchGroupDelete(BinaryMessage message) + { + super(message); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("MOTOROLA DELETE PATCH GROUP:").append(getPatchGroup()); + sb.append(" MSG:").append(getMessage().toHexString()); + + return sb.toString(); + } + + /** + * Patch Group + */ + public Identifier getPatchGroup() + { + if(mPatchGroup == null) + { + PatchGroup patchGroup = new PatchGroup(APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCH_GROUP))); + patchGroup.addPatchedGroups(getPatchedGroups()); + mPatchGroup = APCO25PatchGroup.create(patchGroup); + } + + return mPatchGroup; + } + + public List getPatchedGroups() + { + List patchedGroups = new ArrayList<>(); + + if(hasPatchedGroup1()) + { + patchedGroups.add(getPatchedGroup1()); + } + + if(hasPatchedGroup2()) + { + patchedGroups.add(getPatchedGroup2()); + } + + return patchedGroups; + } + + /** + * Patched Group 1 + */ + public TalkgroupIdentifier getPatchedGroup1() + { + if(mPatchedGroup1 == null) + { + mPatchedGroup1 = APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCHED_GROUP_1)); + } + + return mPatchedGroup1; + } + + public boolean hasPatchedGroup1() + { + return getMessage().getInt(PATCHED_GROUP_1) != 0 && + (getMessage().getInt(PATCH_GROUP) != getMessage().getInt(PATCHED_GROUP_1)); + } + + /** + * Patched Group 2 + */ + public TalkgroupIdentifier getPatchedGroup2() + { + if(mPatchedGroup2 == null) + { + mPatchedGroup2 = APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCHED_GROUP_2)); + } + + return mPatchedGroup2; + } + + public boolean hasPatchedGroup2() + { + return getMessage().getInt(PATCHED_GROUP_2) != 0 && + (getMessage().getInt(PATCH_GROUP) != getMessage().getInt(PATCHED_GROUP_2)); + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUpdate.java new file mode 100644 index 00000000..4ca11908 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUpdate.java @@ -0,0 +1,132 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; + +import java.util.ArrayList; +import java.util.List; + +/** + * Motorola Patch Group Voice Channel Update + */ +public class LCMotorolaPatchGroupVoiceChannelUpdate extends MotorolaLinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] UNKNOWN_1 = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] PATCH_GROUP = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] UNKNOWN_2 = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UNKNOWN_3 = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] FREQUENCY_BAND = {56, 57, 58, 59}; + private static final int[] CHANNEL_NUMBER = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + private APCO25PatchGroup mPatchGroup; + private IChannelDescriptor mChannel; + private List mIdentifiers; + private List mChannels; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCMotorolaPatchGroupVoiceChannelUpdate(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("MOTOROLA PATCH VOICE CHANNEL UPDATE"); + sb.append(" PATCH GROUP:").append(getPatchGroup()); + sb.append(" CHANNEL:").append(getChannel()); + sb.append(" UNK1:").append(getUnknownField1()); + sb.append(" UNK2:").append(getUnknownField2()); + sb.append(" UNK3:").append(getUnknownField3()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + public APCO25PatchGroup getPatchGroup() + { + if(mPatchGroup == null) + { + PatchGroup patchGroup = new PatchGroup(APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCH_GROUP))); + mPatchGroup = APCO25PatchGroup.create(patchGroup); + } + + return mPatchGroup; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public String getUnknownField1() + { + return getMessage().getHex(UNKNOWN_1, 2); + } + public String getUnknownField2() + { + return getMessage().getHex(UNKNOWN_2, 2); + } + public String getUnknownField3() + { + return getMessage().getHex(UNKNOWN_3, 2); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + mChannels.add(getChannel()); + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUser.java new file mode 100644 index 00000000..6773aa27 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaPatchGroupVoiceChannelUser.java @@ -0,0 +1,118 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class LCMotorolaPatchGroupVoiceChannelUser extends MotorolaLinkControlWord +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] PATCH_GROUP_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25PatchGroup mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + public LCMotorolaPatchGroupVoiceChannelUser(BinaryMessage message) + { + super(message); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("MOTOROLA PATCH GROUP VOICE CHANNEL USER FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" ").append(getVoiceServiceOptions()); + sb.append(" MSG:").append(getMessage().toHexString()); + + return sb.toString(); + } + + /** + * Service Options for this channel + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Talkgroup address + */ + public APCO25PatchGroup getGroupAddress() + { + if(mGroupAddress == null) + { + PatchGroup patchGroup = new PatchGroup(APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCH_GROUP_ADDRESS))); + mGroupAddress = APCO25PatchGroup.create(patchGroup); + } + + return mGroupAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaTalkComplete.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaTalkComplete.java new file mode 100644 index 00000000..d67f9fc9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaTalkComplete.java @@ -0,0 +1,115 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Talk complete (but channel will continue to transmit) + */ +public class LCMotorolaTalkComplete extends LinkControlWord +{ + private static final int[] UNKNOWN_FIELD_1 = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] UNKNOWN_FIELD_2 = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71}; + + private Identifier mAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCMotorolaTalkComplete(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("**CRC-FAILED** "); + } + + if(isEncrypted()) + { + sb.append(" ENCRYPTED"); + } + else + { + sb.append("MOTOROLA TALK COMPLETE"); + sb.append(" BY:").append(getAddress()); + sb.append(" UNK1:").append(getUnknownField1()); + sb.append(" UNK2:").append(getUnknownField2()); + sb.append(" MSG:").append(getMessage().toHexString()); + } + return sb.toString(); + } + + public String getUnknownField1() + { + return getMessage().getHex(UNKNOWN_FIELD_1, 2); + } + + public String getUnknownField2() + { + return getMessage().getHex(UNKNOWN_FIELD_2, 2); + } + + /** + * To/From radio identifier communicating with a landline + */ + public Identifier getAddress() + { + if(mAddress == null) + { + mAddress = APCO25AnyTalkgroup.create(getMessage().getInt(ADDRESS)); + } + + return mAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaUnknownOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaUnknownOpcode.java new file mode 100644 index 00000000..8e4e6f50 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/LCMotorolaUnknownOpcode.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown link control word. + */ +public class LCMotorolaUnknownOpcode extends MotorolaLinkControlWord +{ + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCMotorolaUnknownOpcode(BinaryMessage message) + { + super(message); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(" MOTOROLA UNKNOWN/UNRECOGNIZED OPCODE:").append(getOpcode()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/MotorolaLinkControlWord.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/MotorolaLinkControlWord.java new file mode 100644 index 00000000..0ad4ffaa --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/motorola/MotorolaLinkControlWord.java @@ -0,0 +1,37 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +public abstract class MotorolaLinkControlWord extends LinkControlWord +{ + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public MotorolaLinkControlWord(BinaryMessage message) + { + super(message); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcast.java new file mode 100644 index 00000000..efdd966f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcast.java @@ -0,0 +1,168 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast information. + */ +public class LCAdjacentSiteStatusBroadcast extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] LRA = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] RFSS = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] SITE = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] SERVICE_CLASS = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mLRA; + private Identifier mSystem; + private Identifier mRFSS; + private Identifier mSite; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCAdjacentSiteStatusBroadcast(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" RFSS:" + getRfss()); + sb.append(" SITE:" + getSite()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" CHAN:" + getChannel()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getLocationRegistrationArea() + { + if(mLRA == null) + { + mLRA = APCO25Lra.create(getMessage().getInt(LRA)); + } + + return mLRA; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getRfss() + { + if(mRFSS == null) + { + mRFSS = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRFSS; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), + getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getRfss()); + mIdentifiers.add(getSite()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcastExplicit.java new file mode 100644 index 00000000..093414f4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCAdjacentSiteStatusBroadcastExplicit.java @@ -0,0 +1,157 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast explicit information for V/UHF channels + */ +public class LCAdjacentSiteStatusBroadcastExplicit extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] LRA = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] RFSS = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] SITE = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UPLINK_FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] UPLINK_CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] SERVICE_CLASS = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mLRA; + private Identifier mRFSS; + private Identifier mSite; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCAdjacentSiteStatusBroadcastExplicit(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" RFSS:" + getRfss()); + sb.append(" SITE:" + getSite()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" CHAN:" + getChannel()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getLocationRegistrationArea() + { + if(mLRA == null) + { + mLRA = APCO25Lra.create(getMessage().getInt(LRA)); + } + + return mLRA; + } + + public Identifier getRfss() + { + if(mRFSS == null) + { + mRFSS = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRFSS; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getRfss()); + mIdentifiers.add(getSite()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallAlert.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallAlert.java new file mode 100644 index 00000000..180ca27e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallAlert.java @@ -0,0 +1,107 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Echo of the status update from a subscriber when the destination of the update is another subscriber unit. + */ +public class LCCallAlert extends LinkControlWord +{ + private static final int[] RESERVED = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCCallAlert(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + return sb.toString(); + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallTermination.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallTermination.java new file mode 100644 index 00000000..8e59840e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCCallTermination.java @@ -0,0 +1,96 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Termination or cancellation of a call and release of the channel. + */ +public class LCCallTermination extends LinkControlWord +{ + private static final int SYSTEM_CONTROLLER = 0xFFFFFD; + private static final int[] ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71}; + + private Identifier mAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCCallTermination(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" BY:").append(getAddress()); + return sb.toString(); + } + + /** + * To/From radio identifier communicating with a landline + */ + public Identifier getAddress() + { + if(mAddress == null) + { + mAddress = APCO25AnyTalkgroup.create(getMessage().getInt(ADDRESS)); + } + + return mAddress; + } + + /** + * Indicates if the call termination was ordered by the system controller + */ + public boolean isNetworkCommandedTeardown() + { + return getMessage().getInt(ADDRESS) == SYSTEM_CONTROLLER; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCExtendedFunctionCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCExtendedFunctionCommand.java new file mode 100644 index 00000000..223e0e84 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCExtendedFunctionCommand.java @@ -0,0 +1,109 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.ExtendedFunction; + +import java.util.ArrayList; +import java.util.List; + +/** + * Extended function command to a target address + */ +public class LCExtendedFunctionCommand extends LinkControlWord +{ + private static final int[] EXTENDED_FUNCTION = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] EXTENDED_FUNCTION_ARGUMENTS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] TARGET_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCExtendedFunctionCommand(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ").append(getExtendedFunction()); + sb.append(" ARGUMENTS:").append(getExtendedFunctionArguments()); + return sb.toString(); + } + + /** + * Indicates the type of extended function + */ + public ExtendedFunction getExtendedFunction() + { + return ExtendedFunction.fromValue(getMessage().getInt(EXTENDED_FUNCTION)); + } + + /** + * Argument(s). May not be required for the class/function and will be set to null (0) if not required. + */ + public String getExtendedFunctionArguments() + { + return getMessage().getHex(EXTENDED_FUNCTION_ARGUMENTS, 6); + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdate.java new file mode 100644 index 00000000..16bd6433 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdate.java @@ -0,0 +1,148 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.Collections; +import java.util.List; + +/** + * Echo of the status update from a subscriber when the destination of the update is another subscriber unit. + */ +public class LCFrequencyBandUpdate extends LinkControlWord implements IFrequencyBand +{ + private static final int[] FREQUENCY_BAND_IDENTIFIER = {8, 9, 10, 11}; + private static final int[] BANDWIDTH = {12, 13, 14, 15, 16, 17, 18, 19, 20}; + private static final int TRANSMIT_OFFSET_SIGN = 21; + private static final int[] TRANSMIT_OFFSET = {22, 23, 24, 25, 26, 27, 28, 29}; + private static final int[] CHANNEL_SPACING = {30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BASE_FREQUENCY = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCFrequencyBandUpdate(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ID:").append(getIdentifier()); + sb.append(" BW:").append(getBandwidth()); + sb.append(" OFFSET:").append(getTransmitOffset()); + sb.append(" SPACING:").append(getChannelSpacing()); + sb.append(" BASE:").append(getBaseFrequency()); + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER); + } + + @Override + public long getChannelSpacing() + { + return getMessage().getInt(CHANNEL_SPACING) * 125l; + } + + @Override + public long getBaseFrequency() + { + return getMessage().getLong(BASE_FREQUENCY) * 5l; + } + + @Override + public int getBandwidth() + { + return getMessage().getInt(BANDWIDTH) * 125; + } + + @Override + public long getTransmitOffset() + { + long offset = getMessage().getLong(TRANSMIT_OFFSET) * 250000l; + + if(!getMessage().get(TRANSMIT_OFFSET_SIGN)) + { + offset *= -1; + } + + return offset; + } + + /** + * Indicates if the frequency band has a transmit option for the subscriber unit. + */ + public boolean hasTransmitOffset() + { + return getMessage().getInt(TRANSMIT_OFFSET) != 0x80; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (channelNumber * getChannelSpacing()); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + if(hasTransmitOffset()) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + return 0; + } + + @Override + public boolean isTDMA() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 1; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdateExplicit.java new file mode 100644 index 00000000..7bb8a994 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCFrequencyBandUpdateExplicit.java @@ -0,0 +1,160 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.Collections; +import java.util.List; + +/** + * Identifier update explicit + */ +public class LCFrequencyBandUpdateExplicit extends LinkControlWord implements IFrequencyBand +{ + private static final int[] FREQUENCY_BAND_IDENTIFIER = {8, 9, 10, 11}; + private static final int[] BANDWIDTH = {12, 13, 14, 15}; + private static final int TRANSMIT_OFFSET_SIGN = 16; + private static final int[] TRANSMIT_OFFSET = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; + private static final int[] CHANNEL_SPACING = {30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BASE_FREQUENCY = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCFrequencyBandUpdateExplicit(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ID:").append(getIdentifier()); + sb.append(" BW:").append(getBandwidth()); + sb.append(" OFFSET:").append(getTransmitOffset()); + sb.append(" SPACING:").append(getChannelSpacing()); + sb.append(" BASE:").append(getBaseFrequency()); + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER); + } + + @Override + public long getChannelSpacing() + { + return getMessage().getInt(CHANNEL_SPACING) * 125l; + } + + @Override + public long getBaseFrequency() + { + return getMessage().getLong(BASE_FREQUENCY) * 5l; + } + + @Override + public int getBandwidth() + { + int bandwidth = getMessage().getInt(BANDWIDTH); + + if(bandwidth == 0x4) + { + return 6250; + } + else if(bandwidth == 0x5) + { + return 12500; + } + + return 0; + } + + @Override + public long getTransmitOffset() + { + long offset = getMessage().getLong(TRANSMIT_OFFSET) * 250000l; + + if(!getMessage().get(TRANSMIT_OFFSET_SIGN)) + { + offset *= -1; + } + + return offset; + } + + /** + * Indicates if the frequency band has a transmit option for the subscriber unit. + */ + public boolean hasTransmitOffset() + { + return getMessage().getInt(TRANSMIT_OFFSET) != 0x80; + } + + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (channelNumber * getChannelSpacing()); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + if(hasTransmitOffset()) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + return 0; + } + + @Override + public boolean isTDMA() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 1; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupAffiliationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupAffiliationQuery.java new file mode 100644 index 00000000..458020fc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupAffiliationQuery.java @@ -0,0 +1,106 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Current user of an APCO25 channel on both inbound and outbound channels + */ +public class LCGroupAffiliationQuery extends LinkControlWord +{ + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCGroupAffiliationQuery(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + return sb.toString(); + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdate.java new file mode 100644 index 00000000..bae78da1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdate.java @@ -0,0 +1,146 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Update detailing other users/channels that are active on the network. + */ +public class LCGroupVoiceChannelUpdate extends LinkControlWord implements IFrequencyBandReceiver +{ + public static final int[] FREQUENCY_BAND_A = {8, 9, 10, 11}; + public static final int[] CHANNEL_A = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + public static final int[] GROUP_ADDRESS_A = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + public static final int[] FREQUENCY_BAND_B = {40, 41, 42, 43}; + public static final int[] CHANNEL_B = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + public static final int[] GROUP_ADDRESS_B = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + private IChannelDescriptor mChannelA; + private IChannelDescriptor mChannelB; + private Identifier mTalkgroupA; + private Identifier mTalkgroupB; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + */ + public LCGroupVoiceChannelUpdate(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TALKGROUP A:").append(getGroupAddressA()); + sb.append(" CHAN A:").append(getChannelA()); + + if(hasChannelB()) + { + sb.append(" TALKGROUP B:").append(getGroupAddressB()); + sb.append(" CHAN B:").append(getChannelB()); + } + + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddressA()); + + if(hasChannelB()) + { + mIdentifiers.add(getGroupAddressB()); + } + } + + return mIdentifiers; + } + + public IChannelDescriptor getChannelA() + { + if(mChannelA == null) + { + mChannelA = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_A), getMessage().getInt(CHANNEL_A)); + } + + return mChannelA; + } + + public IChannelDescriptor getChannelB() + { + if(mChannelB == null) + { + mChannelB = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_B), getMessage().getInt(CHANNEL_B)); + } + + return mChannelB; + } + + public boolean hasChannelB() + { + return getMessage().getInt(CHANNEL_B) != 0 && getMessage().getInt(GROUP_ADDRESS_A) != getMessage().getInt(GROUP_ADDRESS_B); + } + + public Identifier getGroupAddressA() + { + if(mTalkgroupA == null) + { + mTalkgroupA = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS_A)); + } + + return mTalkgroupA; + } + + public Identifier getGroupAddressB() + { + if(mTalkgroupB == null) + { + mTalkgroupB = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS_B)); + } + + return mTalkgroupB; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannelA()); + channels.add(getChannelB()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdateExplicit.java new file mode 100644 index 00000000..e3d39419 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUpdateExplicit.java @@ -0,0 +1,126 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Update detailing other users/channels that are active on the network. + */ +public class LCGroupVoiceChannelUpdateExplicit extends LinkControlWord implements IFrequencyBandReceiver +{ + public static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + public static final int[] GROUP_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + public static final int[] DOWNLINK_FREQUENCY_BAND = {40, 41, 42, 43}; + public static final int[] DOWNLINK_CHANNEL = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + public static final int[] UPLINK_FREQUENCY_BAND = {56, 57, 58, 59}; + public static final int[] UPLINK_CHANNEL = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + private VoiceServiceOptions mVoiceServiceOptions; + private IChannelDescriptor mChannel; + private List mChannels; + private Identifier mTalkgroup; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + */ + public LCGroupVoiceChannelUpdateExplicit(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TALKGROUP:").append(getGroupAddress()); + sb.append(" ").append(getChannel()); + sb.append(" ").append(getVoiceServiceOptions()); + + return sb.toString(); + } + + /** + * Service options for this channel + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + } + + return mIdentifiers; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL)); + } + + return mChannel; + } + + public Identifier getGroupAddress() + { + if(mTalkgroup == null) + { + mTalkgroup = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mTalkgroup; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUser.java new file mode 100644 index 00000000..f9dc42dc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCGroupVoiceChannelUser.java @@ -0,0 +1,122 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Current user of an APCO25 channel on both inbound and outbound channels + */ +public class LCGroupVoiceChannelUser extends LinkControlWord +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] GROUP_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCGroupVoiceChannelUser(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + /** + * Service Options for this channel + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Talkgroup address + */ + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCMessageUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCMessageUpdate.java new file mode 100644 index 00000000..158ef5ed --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCMessageUpdate.java @@ -0,0 +1,121 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.message.APCO25ShortDataMessage; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Echo of the status update from a subscriber when the destination of the update is another subscriber unit. + */ +public class LCMessageUpdate extends LinkControlWord +{ + private static final int[] MESSAGE = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mShortDataMessage; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCMessageUpdate(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" SDM:").append(getShortDataMessage()); + return sb.toString(); + } + + public Identifier getShortDataMessage() + { + if(mShortDataMessage == null) + { + mShortDataMessage = APCO25ShortDataMessage.create(getMessage().getInt(MESSAGE)); + } + + return mShortDataMessage; + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + mIdentifiers.add(getShortDataMessage()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcast.java new file mode 100644 index 00000000..f17caeb6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcast.java @@ -0,0 +1,140 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Network status broadcast. + */ +public class LCNetworkStatusBroadcast extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] RESERVED = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] WACN = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + private static final int[] SYSTEM = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] SERVICE_CLASS = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mWACN; + private Identifier mSystem; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCNetworkStatusBroadcast(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" CHAN:" + getChannel()); + sb.append(" SERVICE OPTIONS:" + getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), + getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcastExplicit.java new file mode 100644 index 00000000..39d70e4b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCNetworkStatusBroadcastExplicit.java @@ -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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast information. + */ +public class LCNetworkStatusBroadcastExplicit extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] WACN = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; + private static final int[] SYSTEM = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {40, 41, 42, 43}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] UPLINK_FREQUENCY_BAND = {56, 57, 58, 59}; + private static final int[] UPLINK_CHANNEL_NUMBER = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mWACN; + private Identifier mSystem; + private IChannelDescriptor mChannel; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCNetworkStatusBroadcastExplicit(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" CHAN:" + getChannel()); + return sb.toString(); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCProtectionParameterBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCProtectionParameterBroadcast.java new file mode 100644 index 00000000..a2c2bf2e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCProtectionParameterBroadcast.java @@ -0,0 +1,104 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Echo of the status update from a subscriber when the destination of the update is another subscriber unit. + */ +public class LCProtectionParameterBroadcast extends LinkControlWord +{ + private static final int[] RESERVED = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] ALGORITHM_ID = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] KEY_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] TARGET_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mEncryptionKey; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCProtectionParameterBroadcast(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" USE:").append(getEncryptionKey()); + return sb.toString(); + } + + public Identifier getEncryptionKey() + { + if(mEncryptionKey == null) + { + mEncryptionKey = APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID), getMessage().getInt(KEY_ID)); + } + + return mEncryptionKey; + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getEncryptionKey()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcast.java new file mode 100644 index 00000000..0277aa8d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcast.java @@ -0,0 +1,168 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * RF SubSystem broadcast information. + */ +public class LCRFSSStatusBroadcast extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] LRA = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] RFSS = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] SITE = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] SERVICE_CLASS = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mLRA; + private Identifier mSystem; + private Identifier mRFSS; + private Identifier mSite; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCRFSSStatusBroadcast(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" SITE:" + getRfss() + "-" + getSite()); + sb.append(" CHAN:" + getChannel()); + sb.append(" SERVICE OPTIONS:" + getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getLocationRegistrationArea() + { + if(mLRA == null) + { + mLRA = APCO25Lra.create(getMessage().getInt(LRA)); + } + + return mLRA; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getRfss() + { + if(mRFSS == null) + { + mRFSS = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRFSS; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), + getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getRfss()); + mIdentifiers.add(getSite()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcastExplicit.java new file mode 100644 index 00000000..c113ff1b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCRFSSStatusBroadcastExplicit.java @@ -0,0 +1,155 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast information. + */ +public class LCRFSSStatusBroadcastExplicit extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] LRA = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] RFSS = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] SITE = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UPLINK_FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] UPLINK_CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] SERVICE_CLASS = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mLRA; + private Identifier mRFSS; + private Identifier mSite; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCRFSSStatusBroadcastExplicit(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" SITE:" + getRfss() + "-" + getSite()); + sb.append(" CHAN:" + getChannel()); + sb.append(" SERVICE OPTIONS:" + getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getLocationRegistrationArea() + { + if(mLRA == null) + { + mLRA = APCO25Lra.create(getMessage().getInt(LRA)); + } + + return mLRA; + } + + public Identifier getRfss() + { + if(mRFSS == null) + { + mRFSS = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRFSS; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getRfss()); + mIdentifiers.add(getSite()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcast.java new file mode 100644 index 00000000..41a76c73 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcast.java @@ -0,0 +1,181 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast information. + */ +public class LCSecondaryControlChannelBroadcast extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] RFSS = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] SITE = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] FREQUENCY_BAND_A = {24, 25, 26, 27}; + private static final int[] CHANNEL_NUMBER_A = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] SERVICE_CLASS_A = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] FREQUENCY_BAND_B = {48, 49, 50, 51}; + private static final int[] CHANNEL_NUMBER_B = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] SERVICE_CLASS_B = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mRFSS; + private Identifier mSite; + private IChannelDescriptor mChannelA; + private IChannelDescriptor mChannelB; + private VoiceServiceOptions mVoiceServiceOptionsA; + private VoiceServiceOptions mVoiceServiceOptionsB; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCSecondaryControlChannelBroadcast(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" SITE:" + getRFSS() + "-" + getSite()); + sb.append(" CHAN A:" + getChannelA()); + sb.append(" SERVICE OPTIONS:" + getVoiceServiceOptionsA()); + + if(hasChannelB()) + { + sb.append(" CHAN B:" + getChannelB()); + sb.append(" SERVICE OPTIONS:" + getVoiceServiceOptionsB()); + } + return sb.toString(); + } + + public Identifier getRFSS() + { + if(mRFSS == null) + { + mRFSS = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRFSS; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannelA() + { + if(mChannelA == null) + { + mChannelA = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_A), + getMessage().getInt(CHANNEL_NUMBER_A)); + } + + return mChannelA; + } + + private boolean hasChannelB() + { + return getMessage().getInt(CHANNEL_NUMBER_A) != getMessage().getInt(CHANNEL_NUMBER_B) && + getMessage().getInt(SERVICE_CLASS_B) != 0; + } + + public IChannelDescriptor getChannelB() + { + if(mChannelB == null) + { + mChannelB = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_B), + getMessage().getInt(CHANNEL_NUMBER_B)); + } + + return mChannelB; + } + + public VoiceServiceOptions getVoiceServiceOptionsA() + { + if(mVoiceServiceOptionsA == null) + { + mVoiceServiceOptionsA = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS_A)); + } + + return mVoiceServiceOptionsA; + } + + + public VoiceServiceOptions getVoiceServiceOptionsB() + { + if(mVoiceServiceOptionsB == null) + { + mVoiceServiceOptionsB = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS_B)); + } + + return mVoiceServiceOptionsB; + } + + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getRFSS()); + mIdentifiers.add(getSite()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannelA()); + if(hasChannelB()) + { + channels.add(getChannelB()); + } + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcastExplicit.java new file mode 100644 index 00000000..08dae298 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSecondaryControlChannelBroadcastExplicit.java @@ -0,0 +1,141 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast explicit information for V/UHF channels + */ +public class LCSecondaryControlChannelBroadcastExplicit extends LinkControlWord implements IFrequencyBandReceiver +{ + private static final int[] RFSS = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] SITE = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {24, 25, 26, 27}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] UPLINK_FREQUENCY_BAND = {40, 41, 42, 43}; + private static final int[] UPNLINK_CHANNEL_NUMBER = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SERVICE_CLASS = {56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] RESERVED = {64, 65, 66, 67, 68, 69, 70, 71}; + + private List mIdentifiers; + private Identifier mRFSS; + private Identifier mSite; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCSecondaryControlChannelBroadcastExplicit(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" SITE:" + getRFSS() + "-" + getSite()); + sb.append(" CHAN:" + getChannel()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getRFSS() + { + if(mRFSS == null) + { + mRFSS = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRFSS; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPNLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getRFSS()); + mIdentifiers.add(getSite()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusQuery.java new file mode 100644 index 00000000..32042879 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusQuery.java @@ -0,0 +1,106 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Current user of an APCO25 channel on both inbound and outbound channels + */ +public class LCStatusQuery extends LinkControlWord +{ + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCStatusQuery(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + return sb.toString(); + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusUpdate.java new file mode 100644 index 00000000..810ecb89 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCStatusUpdate.java @@ -0,0 +1,135 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Echo of the status update from a subscriber when the destination of the update is another subscriber unit. + */ +public class LCStatusUpdate extends LinkControlWord +{ + private static final int[] UNIT_STATUS = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] USER_STATUS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mUnitStatus; + private Identifier mUserStatus; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCStatusUpdate(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" UNIT:").append(getUnitStatus()); + sb.append(" USER:").append(getUserStatus()); + return sb.toString(); + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null) + { + mUnitStatus = APCO25Status.createUnitStatus(getMessage().getInt(UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null) + { + mUserStatus = APCO25Status.createUserStatus(getMessage().getInt(USER_STATUS)); + } + + return mUserStatus; + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + mIdentifiers.add(getUnitStatus()); + mIdentifiers.add(getUserStatus()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSystemServiceBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSystemServiceBroadcast.java new file mode 100644 index 00000000..0db52d79 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCSystemServiceBroadcast.java @@ -0,0 +1,91 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.Service; + +import java.util.Collections; +import java.util.List; + +/** + * Termination or cancellation of a call and release of the channel. + */ +public class LCSystemServiceBroadcast extends LinkControlWord +{ + private static final int[] REQUEST_PRIORITY_LEVEL = {20, 21, 22, 23}; + private static final int[] AVAILABLE_SERVICES = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47}; + private static final int[] SUPPORTED_SERVICES = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCSystemServiceBroadcast(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" REQUEST PRIORITY LEVEL:").append(getRequestPriorityLevel()); + sb.append(" AVAILABLE SERVICES ").append(getAvailableServices()); + sb.append(" SUPPORTED SERVICES ").append(getSupportedServices()); + return sb.toString(); + } + + /** + * Threshold priority for service requests for this site + */ + public int getRequestPriorityLevel() + { + return getMessage().getInt(REQUEST_PRIORITY_LEVEL); + } + + + public List getAvailableServices() + { + long bitmap = getMessage().getLong(AVAILABLE_SERVICES); + return Service.getServices(bitmap); + } + + public List getSupportedServices() + { + long bitmap = getMessage().getLong(SUPPORTED_SERVICES); + return Service.getServices(bitmap); + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectAnswerRequest.java new file mode 100644 index 00000000..48dd63da --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectAnswerRequest.java @@ -0,0 +1,125 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.telephone.APCO25TelephoneNumber; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.Digit; + +import java.util.ArrayList; +import java.util.List; + +/** + * Request to a radio user to answer a telephone call + */ +public class LCTelephoneInterconnectAnswerRequest extends LinkControlWord +{ + private static final int[] DIGIT_1 = {8, 9, 10, 11}; + private static final int[] DIGIT_2 = {12, 13, 14, 15}; + private static final int[] DIGIT_3 = {16, 17, 18, 19}; + private static final int[] DIGIT_4 = {20, 21, 22, 23}; + private static final int[] DIGIT_5 = {24, 25, 26, 27}; + private static final int[] DIGIT_6 = {28, 29, 30, 31}; + private static final int[] DIGIT_7 = {32, 33, 34, 35}; + private static final int[] DIGIT_8 = {36, 37, 38, 39}; + private static final int[] DIGIT_9 = {40, 41, 42, 43}; + private static final int[] DIGIT_10 = {44, 45, 46, 47}; + private static final int[] TARGET_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mTargetAddress; + private Identifier mTelephoneNumber; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCTelephoneInterconnectAnswerRequest(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" TELEPHONE:").append(getTelephoneNumber()); + + return sb.toString(); + } + + public Identifier getTelephoneNumber() + { + if(mTelephoneNumber == null) + { + List digits = new ArrayList<>(); + digits.add(getMessage().getInt(DIGIT_1)); + digits.add(getMessage().getInt(DIGIT_2)); + digits.add(getMessage().getInt(DIGIT_3)); + digits.add(getMessage().getInt(DIGIT_4)); + digits.add(getMessage().getInt(DIGIT_5)); + digits.add(getMessage().getInt(DIGIT_6)); + digits.add(getMessage().getInt(DIGIT_7)); + digits.add(getMessage().getInt(DIGIT_8)); + digits.add(getMessage().getInt(DIGIT_9)); + digits.add(getMessage().getInt(DIGIT_10)); + + mTelephoneNumber = APCO25TelephoneNumber.createFrom(Digit.decode(digits)); + } + + return mTelephoneNumber; + } + + /** + * To/From radio identifier communicating with a landline + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTelephoneNumber()); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectVoiceChannelUser.java new file mode 100644 index 00000000..ccda878a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCTelephoneInterconnectVoiceChannelUser.java @@ -0,0 +1,118 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Current user of an APCO25 channel on both inbound and outbound channels + */ +public class LCTelephoneInterconnectVoiceChannelUser extends LinkControlWord +{ + private static final int[] RESERVED_1 = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED_2 = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] CALL_TIMER = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCTelephoneInterconnectVoiceChannelUser(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ID:").append(getAddress()); + sb.append(" CALL TIMER:").append(getCallTimerDuration()).append("MS"); + sb.append(" ").append(getVoiceServiceOptions()); + + return sb.toString(); + } + + /** + * Service Options for this channel + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Call timer duration in milliseconds. + */ + public long getCallTimerDuration() + { + //Convert from 100 millisecond intervals to milliseconds. + return getMessage().getInt(CALL_TIMER) * 100; + } + + /** + * To/From radio identifier communicating with a landline + */ + public Identifier getAddress() + { + if(mAddress == null) + { + mAddress = APCO25AnyTalkgroup.create(getMessage().getInt(ADDRESS)); + } + + return mAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitAuthenticationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitAuthenticationCommand.java new file mode 100644 index 00000000..51bc2790 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitAuthenticationCommand.java @@ -0,0 +1,124 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit authentication command + */ +public class LCUnitAuthenticationCommand extends LinkControlWord +{ + private static final int[] WACN = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; + private static final int[] SYSTEM_ID = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] TARGET_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63}; + private static final int[] RESERVED = {64, 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCUnitAuthenticationCommand(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" RADIO:").append(getTargetAddress()); + return sb.toString(); + } + + /** + * WACN + */ + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + /** + * System + */ + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM_ID)); + } + + return mSystem; + } + + /** + * Source address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitRegistrationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitRegistrationCommand.java new file mode 100644 index 00000000..0575e46d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitRegistrationCommand.java @@ -0,0 +1,124 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit registration command + */ +public class LCUnitRegistrationCommand extends LinkControlWord +{ + private static final int[] WACN = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; + private static final int[] SYSTEM_ID = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] TARGET_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63}; + private static final int[] RESERVED = {64, 65, 66, 67, 68, 69, 70, 71}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCUnitRegistrationCommand(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" RADIO:").append(getTargetAddress()); + return sb.toString(); + } + + /** + * WACN + */ + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + /** + * System + */ + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM_ID)); + } + + return mSystem; + } + + /** + * Source address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitAnswerRequest.java new file mode 100644 index 00000000..1a61ddce --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitAnswerRequest.java @@ -0,0 +1,124 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Current user of an APCO25 channel on both inbound and outbound channels + */ +public class LCUnitToUnitAnswerRequest extends LinkControlWord +{ + private static final int[] SERVICE_OPTIONS = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCUnitToUnitAnswerRequest(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" ").append(getVoiceServiceOptions()); + return sb.toString(); + } + + /** + * Service Options for this channel + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitVoiceChannelUser.java new file mode 100644 index 00000000..205db5ca --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/lc/standard/LCUnitToUnitVoiceChannelUser.java @@ -0,0 +1,123 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.lc.standard; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Current user of an APCO25 channel on both inbound and outbound channels + */ +public class LCUnitToUnitVoiceChannelUser extends LinkControlWord +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] TARGET_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public LCUnitToUnitVoiceChannelUser(BinaryMessage message) + { + super(message); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" ").append(getVoiceServiceOptions()); + return sb.toString(); + } + + /** + * Service Options for this channel + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Talkgroup address + */ + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + /** + * Source address + */ + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/EncryptionSyncParameters.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/EncryptionSyncParameters.java new file mode 100644 index 00000000..847c601a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/EncryptionSyncParameters.java @@ -0,0 +1,141 @@ +package io.github.dsheirer.module.decode.p25.message.ldu; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encryption Sync Parameters from Logical Link Data Unit 2 voice frame. + */ +public class EncryptionSyncParameters +{ + private static final int[] MESSAGE_INDICATOR_1 = {0, 1, 2, 3, 4, 5, 6, 7}; + private static final int[] MESSAGE_INDICATOR_2 = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] MESSAGE_INDICATOR_3 = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] MESSAGE_INDICATOR_4 = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] MESSAGE_INDICATOR_5 = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] MESSAGE_INDICATOR_6 = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] MESSAGE_INDICATOR_7 = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] MESSAGE_INDICATOR_8 = {56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] MESSAGE_INDICATOR_9 = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] ALGORITHM_ID = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] KEY_ID = {80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + + private BinaryMessage mMessage; + private boolean mValid = true; + private String mMessageIndicator; + private APCO25EncryptionKey mEncryptionKey; + private List mIdentifiers; + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public EncryptionSyncParameters(BinaryMessage message) + { + mMessage = message; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(isValid()) + { + if(isEncryptedAudio()) + { + sb.append(getEncryptionKey()); + sb.append(" MSG INDICATOR:").append(getMessageIndicator()); + } + else + { + sb.append("UNENCRYPTED "); + } + } + else + { + sb.append("***CRC-FAIL***"); + } + + return sb.toString(); + } + + private BinaryMessage getMessage() + { + return mMessage; + } + + /** + * Indicates if this message is valid or not. + */ + public boolean isValid() + { + return mValid; + } + + /** + * Flags this message as valid or invalid + */ + public void setValid(boolean valid) + { + mValid = valid; + } + + public String getMessageIndicator() + { + if(mMessageIndicator == null) + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_1, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_2, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_3, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_4, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_5, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_6, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_7, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_8, 2)); + sb.append(getMessage().getHex(MESSAGE_INDICATOR_9, 2)); + mMessageIndicator = sb.toString(); + } + + return mMessageIndicator; + } + + public APCO25EncryptionKey getEncryptionKey() + { + if(mEncryptionKey == null) + { + mEncryptionKey = APCO25EncryptionKey.create(getMessage().getInt(ALGORITHM_ID), getMessage().getInt(KEY_ID)); + } + + return mEncryptionKey; + } + + /** + * Indicates if the audio stream is encrypted + */ + public boolean isEncryptedAudio() + { + return getEncryptionKey().getValue().isEncrypted(); + } + + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(isEncryptedAudio()) + { + mIdentifiers.add(getEncryptionKey()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU1Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU1Message.java index a39f2706..1d18872c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU1Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU1Message.java @@ -1,246 +1,218 @@ package io.github.dsheirer.module.decode.p25.message.ldu; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.edac.Hamming10; import io.github.dsheirer.edac.ReedSolomon_63_47_17; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWordFactory; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Vendor; -import io.github.dsheirer.module.decode.p25.reference.VendorLinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class LDU1Message extends LDUMessage +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * LDU Message is formatted as: + * + * IMBE 1: 0000-0143 + * IMBE 2: 0144-0287 + * LC: 0288-0327 + * IMBE 3: 0328-0471 + * LC: 0472-0511 + * IMBE 4: 0512-0655 + * LC: 0656-0695 + * IMBE 5: 0696-0839 + * LC: 0840-0879 + * IMBE 6: 0880-1023 + * LC: 1024-1063 + * IMBE 7: 1064-1207 + * LC: 1208-1247 + * IMBE 8: 1248-1391 + * LSD: 1392-1423 + * IMBE 9: 1424-1567 + */ + +public class LDU1Message extends LDUMessage implements IFrequencyBandReceiver { private final static Logger mLog = LoggerFactory.getLogger(LDU1Message.class); - public static final int ENCRYPTED_LINK_CONTROL_WORD_FLAG = 352; - public static final int IMPLICIT_VENDOR_FLAG = 353; - public static final int[] OPCODE = {354, 355, 356, 357, 362, 363}; - public static final int[] VENDOR = {364, 365, 366, 367, 372, 373, 374, 375}; + private static final int[] GOLAY_WORD_STARTS = {288, 298, 308, 318, 472, 482, 492, 502, 656, 666, 676, 686, + 840, 850, 860, 870, 1024, 1034, 1044, 1054, 1208, 1218, 1228, 1238}; + private static final int[] CW_HEX_0 = {288, 289, 290, 291, 292, 293}; + private static final int[] CW_HEX_1 = {298, 299, 300, 301, 302, 303}; + private static final int[] CW_HEX_2 = {308, 309, 310, 311, 312, 313}; + private static final int[] CW_HEX_3 = {318, 319, 320, 321, 322, 323}; + private static final int[] CW_HEX_4 = {472, 473, 474, 475, 475, 477}; + private static final int[] CW_HEX_5 = {482, 483, 484, 485, 486, 487}; + private static final int[] CW_HEX_6 = {492, 493, 494, 495, 496, 497}; + private static final int[] CW_HEX_7 = {502, 503, 504, 505, 506, 507}; + private static final int[] CW_HEX_8 = {656, 657, 658, 659, 660, 661}; + private static final int[] CW_HEX_9 = {666, 667, 668, 669, 670, 671}; + private static final int[] CW_HEX_10 = {676, 677, 678, 679, 680, 681}; + private static final int[] CW_HEX_11 = {686, 686, 688, 689, 690, 691}; + private static final int[] RS_HEX_0 = {840, 841, 842, 843, 844, 845}; + private static final int[] RS_HEX_1 = {850, 851, 852, 853, 854, 855}; + private static final int[] RS_HEX_2 = {860, 861, 862, 863, 864, 865}; + private static final int[] RS_HEX_3 = {870, 871, 872, 873, 874, 875}; + private static final int[] RS_HEX_4 = {1024, 1025, 1026, 1027, 1028, 1029}; + private static final int[] RS_HEX_5 = {1034, 1035, 1036, 1037, 1038, 1039}; + private static final int[] RS_HEX_6 = {1044, 1045, 1046, 1047, 1048, 1049}; + private static final int[] RS_HEX_7 = {1054, 1055, 1056, 1057, 1058, 1059}; + private static final int[] RS_HEX_8 = {1208, 1209, 1210, 1211, 1212, 1213}; + private static final int[] RS_HEX_9 = {1218, 1219, 1220, 1221, 1222, 1223}; + private static final int[] RS_HEX_10 = {1228, 1229, 1230, 1231, 1232, 1233}; + private static final int[] RS_HEX_11 = {1238, 1239, 1240, 1241, 1242, 1243}; - public static final int[] GOLAY_WORD_STARTS = { - 352, 362, 372, 382, 536, 546, 556, 566, 720, 730, 740, 750, 904, 914, 924, 934, 1088, - 1098, 1108, 1118, 1272, 1282, 1292, 1302}; + private LinkControlWord mLinkControlWord; + private List mIdentifiers; - public static final int[] CW_HEX_0 = {352, 353, 354, 355, 356, 357}; - public static final int[] CW_HEX_1 = {362, 363, 364, 365, 366, 367}; - public static final int[] CW_HEX_2 = {372, 373, 374, 375, 376, 377}; - public static final int[] CW_HEX_3 = {382, 383, 384, 385, 386, 387}; - public static final int[] CW_HEX_4 = {536, 537, 538, 539, 540, 541}; - public static final int[] CW_HEX_5 = {546, 547, 548, 549, 550, 551}; - public static final int[] CW_HEX_6 = {556, 557, 558, 559, 560, 561}; - public static final int[] CW_HEX_7 = {566, 567, 568, 569, 570, 571}; - public static final int[] CW_HEX_8 = {720, 721, 722, 723, 724, 725}; - public static final int[] CW_HEX_9 = {730, 731, 732, 733, 734, 735}; - public static final int[] CW_HEX_10 = {740, 741, 742, 743, 744, 745}; - public static final int[] CW_HEX_11 = {750, 751, 752, 753, 754, 755}; - public static final int[] RS_HEX_0 = {904, 905, 906, 907, 908, 909}; - public static final int[] RS_HEX_1 = {914, 915, 916, 917, 918, 919}; - public static final int[] RS_HEX_2 = {924, 925, 926, 927, 928, 929}; - public static final int[] RS_HEX_3 = {934, 935, 936, 937, 938, 939}; - public static final int[] RS_HEX_4 = {1088, 1089, 1090, 1091, 1092, 1093}; - public static final int[] RS_HEX_5 = {1098, 1099, 1100, 1101, 1102, 1103}; - public static final int[] RS_HEX_6 = {1108, 1109, 1110, 1111, 1112, 1113}; - public static final int[] RS_HEX_7 = {1118, 1119, 1120, 1121, 1122, 1123}; - public static final int[] RS_HEX_8 = {1272, 1273, 1274, 1275, 1276, 1277}; - public static final int[] RS_HEX_9 = {1282, 1283, 1284, 1285, 1286, 1287}; - public static final int[] RS_HEX_10 = {1292, 1293, 1294, 1295, 1296, 1297}; - public static final int[] RS_HEX_11 = {1302, 1303, 1304, 1305, 1306, 1307}; - - /* Reed-Solomon(24,12,13) code protects the link control word. Maximum - * correctable errors are: Hamming Distance(13) / 2 = 6 */ - public static final ReedSolomon_63_47_17 mReedSolomonDecoder = - new ReedSolomon_63_47_17(6); - - public LDU1Message(BinaryMessage message, DataUnitID duid, - AliasList aliasList) + public LDU1Message(CorrectedBinaryMessage message, int nac, long timestamp) { - super(message, duid, aliasList); - - - /* NID CRC is checked in the message framer, thus a constructed message - * means it passed the CRC */ - mCRC = new CRC[3]; - mCRC[0] = CRC.PASSED; - - checkCRC(); + super(message, nac, timestamp); } - /** - * Subclass constructor from an existing LDU1Message - */ - protected LDU1Message(LDU1Message message) + @Override + public DataUnitID getDUID() { - super(message.getSourceMessage(), DataUnitID.LDU1, message.getAliasList()); - - mCRC = message.mCRC; + return DataUnitID.LOGICAL_LINK_DATA_UNIT_1; } - /** - * Indicates if the contents of the Link Control Word are encrypted. - * - * @return true if the contents of the LCW are encrypted. - */ - public boolean isEncryptedLinkControlWord() + public LinkControlWord getLinkControlWord() { - return mMessage.get(ENCRYPTED_LINK_CONTROL_WORD_FLAG); - } - - private void checkCRC() - { - mCRC[1] = CRC.PASSED; - - /* Hamming( 10,6,3 ) error detection and correction */ - for(int index : GOLAY_WORD_STARTS) + if(mLinkControlWord == null) { - int errors = Hamming10.checkAndCorrect(mMessage, index); + createLinkControlWord(); + } - if(errors > 1) + return mLinkControlWord; + } + + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getNAC()); + + if(getLinkControlWord().isValid()) { - mCRC[1] = CRC.FAILED_CRC; + mIdentifiers.addAll(getLinkControlWord().getIdentifiers()); } } - /* Reed-Solomon( 24,16,9 ) error detection and correction - * Check the Reed-Solomon parity bits. The RS decoder expects the code - * words and reed solomon parity hex codewords in reverse order. - * - * Since this is a truncated RS(63) codes, we pad the code with zeros */ + return mIdentifiers; + } + + /** + * Performs error detection and correction on Link Control Word bits and extracts the 72-bit word + */ + private void createLinkControlWord() + { + //Perform error detection and a maximum of one bit error correction using Hamming( 10,6,3 ) + for(int index : GOLAY_WORD_STARTS) + { + //Attempt to fix any single-bit errors + Hamming10.checkAndCorrect(getMessage(), index); + } + + //Perform Reed-Solomon( 24,16,9 ) error detection and correction. Check the Reed-Solomon parity bits. The RS + // decoder expects the code words and reed solomon parity hex codewords in reverse order. Since this is a + // truncated RS(63) codes, we pad the code with zeros int[] input = new int[63]; int[] output = new int[63]; - input[0] = mMessage.getInt(RS_HEX_11); - input[1] = mMessage.getInt(RS_HEX_10); - input[2] = mMessage.getInt(RS_HEX_9); - input[3] = mMessage.getInt(RS_HEX_8); - input[4] = mMessage.getInt(RS_HEX_7); - input[5] = mMessage.getInt(RS_HEX_6); - input[6] = mMessage.getInt(RS_HEX_5); - input[7] = mMessage.getInt(RS_HEX_4); - input[8] = mMessage.getInt(RS_HEX_3); - input[9] = mMessage.getInt(RS_HEX_2); - input[10] = mMessage.getInt(RS_HEX_1); - input[11] = mMessage.getInt(RS_HEX_0); + input[0] = getMessage().getInt(RS_HEX_11); + input[1] = getMessage().getInt(RS_HEX_10); + input[2] = getMessage().getInt(RS_HEX_9); + input[3] = getMessage().getInt(RS_HEX_8); + input[4] = getMessage().getInt(RS_HEX_7); + input[5] = getMessage().getInt(RS_HEX_6); + input[6] = getMessage().getInt(RS_HEX_5); + input[7] = getMessage().getInt(RS_HEX_4); + input[8] = getMessage().getInt(RS_HEX_3); + input[9] = getMessage().getInt(RS_HEX_2); + input[10] = getMessage().getInt(RS_HEX_1); + input[11] = getMessage().getInt(RS_HEX_0); - input[12] = mMessage.getInt(CW_HEX_11); - input[13] = mMessage.getInt(CW_HEX_10); - input[14] = mMessage.getInt(CW_HEX_9); - input[15] = mMessage.getInt(CW_HEX_8); - input[16] = mMessage.getInt(CW_HEX_7); - input[17] = mMessage.getInt(CW_HEX_6); - input[18] = mMessage.getInt(CW_HEX_5); - input[19] = mMessage.getInt(CW_HEX_4); - input[20] = mMessage.getInt(CW_HEX_3); - input[21] = mMessage.getInt(CW_HEX_2); - input[22] = mMessage.getInt(CW_HEX_1); - input[23] = mMessage.getInt(CW_HEX_0); + input[12] = getMessage().getInt(CW_HEX_11); + input[13] = getMessage().getInt(CW_HEX_10); + input[14] = getMessage().getInt(CW_HEX_9); + input[15] = getMessage().getInt(CW_HEX_8); + input[16] = getMessage().getInt(CW_HEX_7); + input[17] = getMessage().getInt(CW_HEX_6); + input[18] = getMessage().getInt(CW_HEX_5); + input[19] = getMessage().getInt(CW_HEX_4); + input[20] = getMessage().getInt(CW_HEX_3); + input[21] = getMessage().getInt(CW_HEX_2); + input[22] = getMessage().getInt(CW_HEX_1); + input[23] = getMessage().getInt(CW_HEX_0); /* indexes 24 - 62 are defaulted to zero */ - boolean irrecoverableErrors = mReedSolomonDecoder.decode(input, output); + //Reed-Solomon(24,12,13) code protects the link control word. Maximum correctable errors are: 6 + ReedSolomon_63_47_17 reedSolomon_63_47_17 = new ReedSolomon_63_47_17(6); + boolean irrecoverableErrors = reedSolomon_63_47_17.decode(input, output); + + //Transfer error corrected output to a new binary message + BinaryMessage binaryMessage = new BinaryMessage(72); + + int pointer = 0; + + for(int x = 23; x >= 12; x--) + { + if(output[x] != -1) + { + binaryMessage.load(pointer, 6, output[x]); + } + else + { + mLog.error("-1 output"); + } + + pointer += 6; + } + + mLinkControlWord = LinkControlWordFactory.create(binaryMessage); if(irrecoverableErrors) { - mCRC[2] = CRC.FAILED_CRC; + mLinkControlWord.setValid(false); } - else + + //If we corrected any bit errors, update the original message with the bit error count + for(int x = 0; x < 23; x++) { - /* Since we've detected that we can correct the RS words, mark the - * hamming crc as corrected as well, if it is currently failed */ - if(mCRC[1] == CRC.FAILED_CRC) + if(output[x] != input[x]) { - mCRC[1] = CRC.CORRECTED; + getMessage().incrementCorrectedBitCount(Integer.bitCount((output[x] ^ input[x]))); } - - mCRC[2] = CRC.PASSED; - - /* Only fix the codeword data hex codewords */ - repairHexCodeword(input, output, 12, CW_HEX_11); - repairHexCodeword(input, output, 13, CW_HEX_10); - repairHexCodeword(input, output, 14, CW_HEX_9); - repairHexCodeword(input, output, 15, CW_HEX_8); - repairHexCodeword(input, output, 16, CW_HEX_7); - repairHexCodeword(input, output, 17, CW_HEX_6); - repairHexCodeword(input, output, 18, CW_HEX_5); - repairHexCodeword(input, output, 19, CW_HEX_4); - repairHexCodeword(input, output, 20, CW_HEX_3); - repairHexCodeword(input, output, 21, CW_HEX_2); - repairHexCodeword(input, output, 22, CW_HEX_1); - repairHexCodeword(input, output, 23, CW_HEX_0); - } - } - - private void repairHexCodeword(int[] input, int[] output, int index, int[] indexSet) - { - if(input[index] != output[index]) - { - mMessage.load(indexSet[0], 6, output[index]); - mCRC[2] = CRC.CORRECTED; } } @Override - public String getMessage() + public List getChannels() + { + if(getLinkControlWord().isValid() && getLinkControlWord() instanceof IFrequencyBandReceiver) + { + return ((IFrequencyBandReceiver) getLinkControlWord()).getChannels(); + } + + return Collections.EMPTY_LIST; + } + + @Override + public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getMessageStub()); - sb.append(" "); - sb.append(mMessage.toString()); + sb.append(" ").append(getLinkControlWord()); return sb.toString(); } - - public String getMessageStub() - { - StringBuilder sb = new StringBuilder(); - - sb.append(super.getMessageStub()); - - if(isEncryptedLinkControlWord()) - { - sb.append("ENCRYPTED"); - } - else - { - if(isImplicitFormat() || getVendor() == Vendor.STANDARD) - { - sb.append(getOpcode().getLabel()); - } - else - { - sb.append("VENDOR:" + getVendor().getLabel() + " "); - sb.append(getVendorOpcode().getLabel()); - } - } - - return sb.toString(); - } - - public boolean isImplicitFormat() - { - return mMessage.get(IMPLICIT_VENDOR_FLAG); - } - - public LinkControlOpcode getOpcode() - { - return LinkControlOpcode.fromValue(mMessage.getInt(OPCODE)); - } - - public VendorLinkControlOpcode getVendorOpcode() - { - return VendorLinkControlOpcode.fromValue(mMessage.getInt(OPCODE)); - } - - public Vendor getVendor() - { - if(isImplicitFormat()) - { - return Vendor.STANDARD; - } - - return Vendor.fromValue(mMessage.getInt(VENDOR)); - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU2Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU2Message.java index ec72fe01..45b29866 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU2Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDU2Message.java @@ -1,231 +1,182 @@ package io.github.dsheirer.module.decode.p25.message.ldu; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.Hamming10; import io.github.dsheirer.edac.ReedSolomon_63_47_17; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + public class LDU2Message extends LDUMessage { - private final static Logger mLog = LoggerFactory.getLogger( LDU2Message.class ); + private final static Logger mLog = LoggerFactory.getLogger(LDU2Message.class); - public static final int[] MESSAGE_INDICATOR_A = { 352,353,354,355,356,357, - 362,363,364,365,366,367,372,373,374,375,376,377,382,383,384,385,386,387, - 536,537,538,539,540,541,546,547,548,549,550,551 }; - public static final int[] MESSAGE_INDICATOR_B = { 556,557,558,559,560,561, - 566,567,568,569,570,571,720,721,722,723,724,725,730,731,732,733,734,735, - 740,741,742,743,744,745,750,751,752,753,754,755 }; - public static final int[] ALGORITHM_ID = { 904,905,906,907,908,909,914,915 }; - public static final int[] KEY_ID = { 916,917,918,919,924,925,926,927,928, - 929,934,935,936,937,938,939 }; + private static final int[] GOLAY_WORD_STARTS = {288, 298, 308, 318, 472, 482, 492, 502, 656, 666, 676, 686, + 840, 850, 860, 870, 1024, 1034, 1044, 1054, 1208, 1218, 1228, 1238}; + private static final int[] CW_HEX_0 = {288, 289, 290, 291, 292, 293}; + private static final int[] CW_HEX_1 = {298, 299, 300, 301, 302, 303}; + private static final int[] CW_HEX_2 = {308, 309, 310, 311, 312, 313}; + private static final int[] CW_HEX_3 = {318, 319, 320, 321, 322, 323}; + private static final int[] CW_HEX_4 = {472, 473, 474, 475, 475, 477}; + private static final int[] CW_HEX_5 = {482, 483, 484, 485, 486, 487}; + private static final int[] CW_HEX_6 = {492, 493, 494, 495, 496, 497}; + private static final int[] CW_HEX_7 = {502, 503, 504, 505, 506, 507}; + private static final int[] CW_HEX_8 = {656, 657, 658, 659, 660, 661}; + private static final int[] CW_HEX_9 = {666, 667, 668, 669, 670, 671}; + private static final int[] CW_HEX_10 = {676, 677, 678, 679, 680, 681}; + private static final int[] CW_HEX_11 = {686, 686, 688, 689, 690, 691}; + private static final int[] CW_HEX_12 = {840, 841, 842, 843, 844, 845}; + private static final int[] CW_HEX_13 = {850, 851, 852, 853, 854, 855}; + private static final int[] CW_HEX_14 = {860, 861, 862, 863, 864, 865}; + private static final int[] CW_HEX_15 = {870, 871, 872, 873, 874, 875}; + private static final int[] RS_HEX_0 = {1024, 1025, 1026, 1027, 1028, 1029}; + private static final int[] RS_HEX_1 = {1034, 1035, 1036, 1037, 1038, 1039}; + private static final int[] RS_HEX_2 = {1044, 1045, 1046, 1047, 1048, 1049}; + private static final int[] RS_HEX_3 = {1054, 1055, 1056, 1057, 1058, 1059}; + private static final int[] RS_HEX_4 = {1208, 1209, 1210, 1211, 1212, 1213}; + private static final int[] RS_HEX_5 = {1218, 1219, 1220, 1221, 1222, 1223}; + private static final int[] RS_HEX_6 = {1228, 1229, 1230, 1231, 1232, 1233}; + private static final int[] RS_HEX_7 = {1238, 1239, 1240, 1241, 1242, 1243}; - public static final int[] GOLAY_WORD_STARTS = { - 352,362,372,382,536,546,556,566,720,730,740,750,904,914,924,934,1088, - 1098,1108,1118,1272,1282,1292,1302 }; + private EncryptionSyncParameters mEncryptionSyncParameters; + private List mIdentifiers; - public static final int[] CW_HEX_0 = { 352,353,354,355,356,357 }; - public static final int[] CW_HEX_1 = { 362,363,364,365,366,367 }; - public static final int[] CW_HEX_2 = { 372,373,374,375,376,377 }; - public static final int[] CW_HEX_3 = { 382,383,384,385,386,387 }; - public static final int[] CW_HEX_4 = { 536,537,538,539,540,541 }; - public static final int[] CW_HEX_5 = { 546,547,548,549,550,551 }; - public static final int[] CW_HEX_6 = { 556,557,558,559,560,561 }; - public static final int[] CW_HEX_7 = { 566,567,568,569,570,571 }; - public static final int[] CW_HEX_8 = { 720,721,722,723,724,725 }; - public static final int[] CW_HEX_9 = { 730,731,732,733,734,735 }; - public static final int[] CW_HEX_10 = { 740,741,742,743,744,745 }; - public static final int[] CW_HEX_11 = { 750,751,752,753,754,755 }; - public static final int[] CW_HEX_12 = { 904,905,906,907,908,909 }; - public static final int[] CW_HEX_13 = { 914,915,916,917,918,919 }; - public static final int[] CW_HEX_14 = { 924,925,926,927,928,929 }; - public static final int[] CW_HEX_15 = { 934,935,936,937,938,939 }; - public static final int[] RS_HEX_0 = { 1088,1089,1090,1091,1092,1093 }; - public static final int[] RS_HEX_1 = { 1098,1099,1100,1101,1102,1103 }; - public static final int[] RS_HEX_2 = { 1108,1109,1110,1111,1112,1113 }; - public static final int[] RS_HEX_3 = { 1118,1119,1120,1121,1122,1123 }; - public static final int[] RS_HEX_4 = { 1272,1273,1274,1275,1276,1277 }; - public static final int[] RS_HEX_5 = { 1282,1283,1284,1285,1286,1287 }; - public static final int[] RS_HEX_6 = { 1292,1293,1294,1295,1296,1297 }; - public static final int[] RS_HEX_7 = { 1302,1303,1304,1305,1306,1307 }; - - /* Reed-Solomon(24,16,9) code protects the encryption sync word. Maximum - * correctable errors are: Hamming Distance(9) / 2 = 4 */ - public static final ReedSolomon_63_47_17 mReedSolomonDecoder = - new ReedSolomon_63_47_17( 4 ); - - public LDU2Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + public LDU2Message(CorrectedBinaryMessage message, int nac, long timestamp) { - super( message, duid, aliasList ); - - /* NID CRC is checked in the message framer, thus a constructed message - * means it passed the CRC */ - mCRC = new CRC[ 3 ]; - mCRC[ 0 ] = CRC.PASSED; - - checkCRC(); + super(message, nac, timestamp); } - /** - * Indicates if the audio stream is encrypted - */ - public boolean isEncryptedAudio() - { - Encryption encryption = getEncryption(); - - return encryption != Encryption.UNENCRYPTED && - encryption != Encryption.UNKNOWN; - } - - private void checkCRC() - { - mCRC[ 1 ] = CRC.PASSED; + @Override + public DataUnitID getDUID() + { + return DataUnitID.LOGICAL_LINK_DATA_UNIT_2; + } - /* Hamming( 10,6,3 ) error detection and correction */ - for( int index: GOLAY_WORD_STARTS ) - { - int errors = Hamming10.checkAndCorrect( mMessage, index ); - - if( errors > 1 ) - { - mCRC[ 1 ] = CRC.FAILED_CRC; - } - } + /** + * Encryption sync parameters allow a user to join an encrypted audio call. + */ + public EncryptionSyncParameters getEncryptionSyncParameters() + { + if(mEncryptionSyncParameters == null) + { + createEncryptionSyncParameters(); + } - /* Reed-Solomon( 24,16,9 ) error detection and correction - * Check the Reed-Solomon parity bits. The RS decoder expects the code - * words and reed solomon parity hex codewords in reverse order. - * - * Since this is a truncated RS(63) codes, we pad the code with zeros */ - int[] input = new int [63]; - int[] output = new int [63]; - - input[ 0 ] = mMessage.getInt( RS_HEX_7 ); - input[ 1 ] = mMessage.getInt( RS_HEX_6 ); - input[ 2 ] = mMessage.getInt( RS_HEX_5 ); - input[ 3 ] = mMessage.getInt( RS_HEX_4 ); - input[ 4 ] = mMessage.getInt( RS_HEX_3 ); - input[ 5 ] = mMessage.getInt( RS_HEX_2 ); - input[ 6 ] = mMessage.getInt( RS_HEX_1 ); - input[ 7 ] = mMessage.getInt( RS_HEX_0 ); - - input[ 8 ] = mMessage.getInt( CW_HEX_15 ); - input[ 9 ] = mMessage.getInt( CW_HEX_14 ); - input[ 10 ] = mMessage.getInt( CW_HEX_13 ); - input[ 11 ] = mMessage.getInt( CW_HEX_12 ); - input[ 12 ] = mMessage.getInt( CW_HEX_11 ); - input[ 13 ] = mMessage.getInt( CW_HEX_10 ); - input[ 14 ] = mMessage.getInt( CW_HEX_9 ); - input[ 15 ] = mMessage.getInt( CW_HEX_8 ); - input[ 16 ] = mMessage.getInt( CW_HEX_7 ); - input[ 17 ] = mMessage.getInt( CW_HEX_6 ); - input[ 18 ] = mMessage.getInt( CW_HEX_5 ); - input[ 19 ] = mMessage.getInt( CW_HEX_4 ); - input[ 20 ] = mMessage.getInt( CW_HEX_3 ); - input[ 21 ] = mMessage.getInt( CW_HEX_2 ); - input[ 22 ] = mMessage.getInt( CW_HEX_1 ); - input[ 23 ] = mMessage.getInt( CW_HEX_0 ); + return mEncryptionSyncParameters; + } + + /** + * Performs error detection and correction against the encryption sync parameters portion of the message and + * extracts that message into an encryption sync parameters instance. + */ + private void createEncryptionSyncParameters() + { + /* Hamming( 10,6,3 ) error detection and correction */ + for(int index : GOLAY_WORD_STARTS) + { + int errors = Hamming10.checkAndCorrect(getMessage(), index); + } + + /* Reed-Solomon( 24,16,9 ) error detection and correction + * Check the Reed-Solomon parity bits. The RS decoder expects the code + * words and reed solomon parity hex codewords in reverse order. + * + * Since this is a truncated RS(63) codes, we pad the code with zeros */ + int[] input = new int[63]; + int[] output = new int[63]; + + input[0] = getMessage().getInt(RS_HEX_7); + input[1] = getMessage().getInt(RS_HEX_6); + input[2] = getMessage().getInt(RS_HEX_5); + input[3] = getMessage().getInt(RS_HEX_4); + input[4] = getMessage().getInt(RS_HEX_3); + input[5] = getMessage().getInt(RS_HEX_2); + input[6] = getMessage().getInt(RS_HEX_1); + input[7] = getMessage().getInt(RS_HEX_0); + + input[8] = getMessage().getInt(CW_HEX_15); + input[9] = getMessage().getInt(CW_HEX_14); + input[10] = getMessage().getInt(CW_HEX_13); + input[11] = getMessage().getInt(CW_HEX_12); + input[12] = getMessage().getInt(CW_HEX_11); + input[13] = getMessage().getInt(CW_HEX_10); + input[14] = getMessage().getInt(CW_HEX_9); + input[15] = getMessage().getInt(CW_HEX_8); + input[16] = getMessage().getInt(CW_HEX_7); + input[17] = getMessage().getInt(CW_HEX_6); + input[18] = getMessage().getInt(CW_HEX_5); + input[19] = getMessage().getInt(CW_HEX_4); + input[20] = getMessage().getInt(CW_HEX_3); + input[21] = getMessage().getInt(CW_HEX_2); + input[22] = getMessage().getInt(CW_HEX_1); + input[23] = getMessage().getInt(CW_HEX_0); /* indexes 24 - 62 are defaulted to zero */ - boolean irrecoverableErrors = mReedSolomonDecoder.decode( input, output ); + //Reed-Solomon(24,16,9) code protects the encryption sync word. Maximum correctable errors are: 4 + ReedSolomon_63_47_17 reedSolomon_63_47_17 = new ReedSolomon_63_47_17(4); + boolean irrecoverableErrors = reedSolomon_63_47_17.decode(input, output); - if( irrecoverableErrors ) + BinaryMessage binaryMessage = new BinaryMessage(96); + + int pointer = 0; + + for(int x = 23; x >= 8; x--) { - mCRC[ 2 ] = CRC.FAILED_CRC; + if(output[x] != -1) + { + binaryMessage.load(pointer, 6, output[x]); + } + + pointer += 6; + } + + mEncryptionSyncParameters = new EncryptionSyncParameters(binaryMessage); + + if(irrecoverableErrors) + { + mEncryptionSyncParameters.setValid(false); } else { - /* Since we've detected that we can correct the RS words, mark the - * hamming crc as corrected as well, if it is currently failed */ - if( mCRC[ 1 ] == CRC.FAILED_CRC ) - { - mCRC[ 1 ] = CRC.CORRECTED; - } - - mCRC[ 2 ] = CRC.PASSED; - - /* Only fix the codeword data hex codewords */ - repairHexCodeword( input, output, 8, CW_HEX_15 ); - repairHexCodeword( input, output, 9, CW_HEX_14 ); - repairHexCodeword( input, output, 10, CW_HEX_13 ); - repairHexCodeword( input, output, 11, CW_HEX_12 ); - repairHexCodeword( input, output, 12, CW_HEX_11 ); - repairHexCodeword( input, output, 13, CW_HEX_10 ); - repairHexCodeword( input, output, 14, CW_HEX_9 ); - repairHexCodeword( input, output, 15, CW_HEX_8 ); - repairHexCodeword( input, output, 16, CW_HEX_7 ); - repairHexCodeword( input, output, 17, CW_HEX_6 ); - repairHexCodeword( input, output, 18, CW_HEX_5 ); - repairHexCodeword( input, output, 19, CW_HEX_4 ); - repairHexCodeword( input, output, 20, CW_HEX_3 ); - repairHexCodeword( input, output, 21, CW_HEX_2 ); - repairHexCodeword( input, output, 22, CW_HEX_1 ); - repairHexCodeword( input, output, 23, CW_HEX_0 ); + //If we corrected any bit errors, update the original message with the bit error count + for(int x = 0; x < 23; x++) + { + if(output[x] != input[x]) + { + getMessage().incrementCorrectedBitCount(Integer.bitCount((output[x] ^ input[x]))); + } + } } - } - - private void repairHexCodeword( int[] input, int[] output, int index, int[] indexSet ) - { - if( input[ index ] != output[ index ] ) - { - mMessage.load( indexSet[ 0 ], 6, output[ index ] ); - mCRC[ 2 ] = CRC.CORRECTED; - } - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - - Encryption encryption = getEncryption(); - - if( encryption != Encryption.UNENCRYPTED ) - { - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - - sb.append( " ENCRYPTED VOICE:" ); - sb.append( encryption.name() ); - sb.append( " KEY ID:" + getKeyID() ); - sb.append( " MSG INDICATOR:" + getMessageIndicator() ); - } - else - { - sb.append( super.getMessageStub() ); - sb.append( "UNENCRYPTED" ); - } - - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); } - - public String getMessageIndicator() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getHex( MESSAGE_INDICATOR_A, 9 ) ); - sb.append( mMessage.getHex( MESSAGE_INDICATOR_B, 9 ) ); - return sb.toString(); - } - - public Encryption getEncryption() - { - return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); - } - - public int getKeyID() - { - return mMessage.getInt( KEY_ID ); - } + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getNAC()); + + if(getEncryptionSyncParameters().isValid()) + { + mIdentifiers.addAll(getEncryptionSyncParameters().getIdentifiers()); + } + } + + return mIdentifiers; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ").append(getEncryptionSyncParameters()); + + return sb.toString(); + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDUMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDUMessage.java index 149f7dc2..bbb17d36 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDUMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/LDUMessage.java @@ -1,9 +1,7 @@ package io.github.dsheirer.module.decode.p25.message.ldu; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,78 +10,64 @@ import java.util.List; public abstract class LDUMessage extends P25Message { - private final static Logger mLog = LoggerFactory.getLogger( LDUMessage.class ); + private final static Logger mLog = LoggerFactory.getLogger(LDUMessage.class); - public static final int IMBE_FRAME_1 = 64; - public static final int IMBE_FRAME_2 = 208; - public static final int IMBE_FRAME_3 = 392; - public static final int IMBE_FRAME_4 = 576; - public static final int IMBE_FRAME_5 = 760; - public static final int IMBE_FRAME_6 = 944; - public static final int IMBE_FRAME_7 = 1128; - public static final int IMBE_FRAME_8 = 1312; - public static final int IMBE_FRAME_9 = 1488; + public static final int IMBE_FRAME_1 = 0; + public static final int IMBE_FRAME_2 = 144; + public static final int IMBE_FRAME_3 = 328; + public static final int IMBE_FRAME_4 = 512; + public static final int IMBE_FRAME_5 = 696; + public static final int IMBE_FRAME_6 = 880; + public static final int IMBE_FRAME_7 = 1064; + public static final int IMBE_FRAME_8 = 1248; + public static final int IMBE_FRAME_9 = 1424; - public static final int[] LOW_SPEED_DATA = { 1456,1457,1458,1459,1460,1461, - 1462,1463,1472,1473,1474,1475,1476,1477,1478,1479 }; + public static final int[] LOW_SPEED_DATA = {1392, 1393, 1394, 1395, 1396, 1397, + 1398, 1399, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415}; - public LDUMessage( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) + public LDUMessage(CorrectedBinaryMessage message, int nac, long timestamp) { - super( message, duid, aliasList ); + super(message, nac, timestamp); } - - @Override - public String getMessage() + + /** + * Low speed data field contents. + */ + public String getLowSpeedData() { - return getMessageStub(); + return getMessage().getHex(LOW_SPEED_DATA, 4); } - + public String getMessageStub() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - sb.append( " VOICE LSD:" ); - sb.append( getLowSpeedData() ); - sb.append( " " ); - - return sb.toString(); + sb.append(super.getMessageStub()); + sb.append(" VOICE LSD:"); + sb.append(getLowSpeedData()); + + return sb.toString(); } - - public String getLowSpeedData() - { - return mMessage.getHex( LOW_SPEED_DATA, 4 ); - } - public boolean isValid() - { -// return mCRC[ 2 ] != null && mCRC[ 2 ] != CRC.FAILED_CRC; - return true; - } + /** + * Returns a 162 byte array containing 9 IMBE voice frames of 18-bytes + * (144-bits) each. Each frame is intact as transmitted and requires + * deinterleaving, error correction, derandomizing, etc. + */ + public List getIMBEFrames() + { + List frames = new ArrayList(); - /** - * Returns a 162 byte array containing 9 IMBE voice frames of 18-bytes - * (144-bits) each. Each frame is intact as transmitted and requires - * deinterleaving, error correction, derandomizing, etc. - */ - public List getIMBEFrames() - { - List frames = new ArrayList(); - - frames.add( mMessage.get( IMBE_FRAME_1, IMBE_FRAME_1 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_2, IMBE_FRAME_2 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_3, IMBE_FRAME_3 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_4, IMBE_FRAME_4 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_5, IMBE_FRAME_5 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_6, IMBE_FRAME_6 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_7, IMBE_FRAME_7 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_8, IMBE_FRAME_8 + 144 ).toByteArray() ); - frames.add( mMessage.get( IMBE_FRAME_9, IMBE_FRAME_9 + 144 ).toByteArray() ); + frames.add(getMessage().get(IMBE_FRAME_1, IMBE_FRAME_1 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_2, IMBE_FRAME_2 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_3, IMBE_FRAME_3 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_4, IMBE_FRAME_4 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_5, IMBE_FRAME_5 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_6, IMBE_FRAME_6 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_7, IMBE_FRAME_7 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_8, IMBE_FRAME_8 + 144).toByteArray()); + frames.add(getMessage().get(IMBE_FRAME_9, IMBE_FRAME_9 + 144).toByteArray()); - return frames; - } + return frames; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java deleted file mode 100644 index c1684e42..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java +++ /dev/null @@ -1,172 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class AdjacentSiteStatusBroadcast extends LDU1Message implements IdentifierReceiver, IAdjacentSite -{ - public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; - public static final int[] SYSTEM_ID = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; - public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; - public static final int[] IDENTIFIER = {720, 721, 722, 723}; - public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; - public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mIdentifierUpdate; - - public AdjacentSiteStatusBroadcast(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLRA()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); - - sb.append(" CHAN:" + getChannel()); - - sb.append(" " + getSystemServiceClass()); - - return sb.toString(); - } - - public String getUniqueID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getSystemID()); - sb.append(":"); - sb.append(getRFSS()); - sb.append(":"); - sb.append(getSiteID()); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex(LRA, 2); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSS() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public String getSystemServiceClass() - { - return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } - - - @Override - public String getDownlinkChannel() - { - return getChannel(); - } - - @Override - public String getUplinkChannel() - { - return getChannel(); - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java deleted file mode 100644 index 7543f785..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver, IAdjacentSite -{ - public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; - public static final int[] TRANSMIT_IDENTIFIER = {376, 377, 382, 383}; - public static final int[] TRANSMIT_CHANNEL = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; - public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; - public static final int[] RECEIVE_IDENTIFIER = {720, 721, 722, 723}; - public static final int[] RECEIVE_CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; - - private IBandIdentifier mTransmitBand; - private IBandIdentifier mReceiveBand; - - public AdjacentSiteStatusBroadcastExplicit(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLRA()); - - sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); - - sb.append(" DNLINK:" + getDownlinkChannel()); - - sb.append(" UPLINK:" + getUplinkChannel()); - - return sb.toString(); - } - - public String getUniqueID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getSystemID()); - sb.append(":"); - sb.append(getRFSS()); - sb.append(":"); - sb.append(getSiteID()); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex(LRA, 2); - } - - public String getRFSS() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSystemID() - { - return "***"; - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannel() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getDownlinkChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannel(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannel() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getUplinkChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannel(); - } - - @Override - public String getSystemServiceClass() - { - return "[]"; - } - - public long getDownlinkFrequency() - { - if(mTransmitBand != null) - { - return mTransmitBand.getDownlinkFrequency(getTransmitChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveBand != null) - { - return mReceiveBand.getUplinkFrequency(getReceiveChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitBand = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveBand = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallAlert.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallAlert.java deleted file mode 100644 index 034b1dcd..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallAlert.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class CallAlert extends LDU1Message -{ - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public CallAlert( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CALL_ALERT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallTermination.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallTermination.java deleted file mode 100644 index 7bc4e929..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/CallTermination.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class CallTermination extends LDU1Message -{ - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public CallTermination( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" + getSourceAddress() ); - - return sb.toString(); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java deleted file mode 100644 index 8654d9e5..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class ChannelIdentifierUpdate extends LDU1Message implements IBandIdentifier -{ - public static final int[] IDENTIFIER = {364, 365, 366, 367}; - public static final int[] BANDWIDTH = {372, 373, 374, 375, 376, 377, 382, 383, 384}; - public static final int[] TRANSMIT_OFFSET = {385, 386, 387, 536, 537, 538, 539, - 540, 541}; - public static final int[] CHANNEL_SPACING = {546, 547, 548, 549, 550, 551, 556, - 557, 558, 559}; - public static final int[] BASE_FREQUENCY = {560, 561, 566, 567, 568, 569, 570, - 571, 720, 721, 722, 723, 724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743, 744, - 745, 750, 751, 752, 753, 754, 755}; - - public ChannelIdentifierUpdate(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" IDEN:" + getIdentifier()); - - sb.append(" BASE:" + getBaseFrequency()); - - sb.append(" BW:" + getBandwidth()); - - sb.append(" SPACING:" + getChannelSpacing()); - - sb.append(" OFFSET:" + getTransmitOffset()); - - return sb.toString(); - } - - @Override - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - /** - * Channel bandwidth in hertz - */ - @Override - public int getBandwidth() - { - return mMessage.getInt(BANDWIDTH) * 125; - } - - @Override - public long getChannelSpacing() - { - return mMessage.getLong(CHANNEL_SPACING) * 125l; - } - - @Override - public long getBaseFrequency() - { - return mMessage.getLong(BASE_FREQUENCY) * 5l; - } - - @Override - public long getTransmitOffset() - { - return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l; - } - - @Override - public long getDownlinkFrequency(int channelNumber) - { - return getBaseFrequency() + (channelNumber * getChannelSpacing()); - } - - @Override - public long getUplinkFrequency(int channelNumber) - { - return getDownlinkFrequency(channelNumber) + getTransmitOffset(); - } - - @Override - public boolean isTDMA() - { - return false; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java deleted file mode 100644 index 7945971b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class ChannelIdentifierUpdateExplicit extends LDU1Message implements IBandIdentifier -{ - public static final int[] IDENTIFIER = { 364,365,366,367 }; - public static final int[] BANDWIDTH = { 372,373,374,375 }; - public static final int[] TRANSMIT_OFFSET = { 376,377,382,383,384,385,386, - 387,536,537,538,539,540,541 }; - public static final int[] CHANNEL_SPACING = { 546,547,548,549,550,551,556, - 557,558,559 }; - public static final int[] BASE_FREQUENCY = { 560,561,566,567,568,569,570, - 571,720,721,722,723,724,725,730,731,732,733,734,735,740,741,742,743,744, - 745,750,751,752,753,754,755 }; - - public ChannelIdentifierUpdateExplicit( LDU1Message source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " IDEN:" + getIdentifier() ); - - sb.append( " BASE:" + getBaseFrequency() ); - - sb.append( " BW:" + getBandwidth() ); - - sb.append( " SPACING:" + getChannelSpacing() ); - - sb.append( " OFFSET:" + getTransmitOffset() ); - - return sb.toString(); - } - - @Override - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - /** - * Channel bandwidth in hertz - */ - @Override - public int getBandwidth() - { - return mMessage.getInt( BANDWIDTH ) * 125; - } - - @Override - public long getChannelSpacing() - { - return mMessage.getLong( CHANNEL_SPACING ) * 125l; - } - - @Override - public long getBaseFrequency() - { - return mMessage.getLong( BASE_FREQUENCY ) * 5l; - } - - @Override - public long getTransmitOffset() - { - return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l; - } - - @Override - public long getDownlinkFrequency(int channelNumber) - { - return getBaseFrequency() + ( channelNumber * getChannelSpacing() ); - } - - @Override - public long getUplinkFrequency(int channelNumber) - { - return getDownlinkFrequency(channelNumber) + getTransmitOffset(); - } - - @Override - public boolean isTDMA() - { - return false; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java deleted file mode 100644 index 77fa4256..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.ExtendedFunction; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ExtendedFunctionCommand extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( ExtendedFunctionCommand.class ); - public static final int[] EXTENDED_FUNCTION = { 364,365,366,367,372,373,374, - 375,376,377,382,383,384,385,386,387 }; - public static final int[] ARGUMENT = { 536,537,538,539,540,541,546,547,548, - 549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] TARGET_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public ExtendedFunctionCommand( LDU1Message source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.EXTENDED_FUNCTION_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " EXTENDED FUNCTION:" + getExtendedFunction().getLabel() ); - - sb.append( " ARGUMENT: " + getTargetAddress() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public ExtendedFunction getExtendedFunction() - { - return ExtendedFunction.fromValue( mMessage.getInt( EXTENDED_FUNCTION ) ); - } - - public String getArgument() - { - return mMessage.getHex( ARGUMENT, 6 ); - } - - public String getSourceAddress() - { - switch( getExtendedFunction() ) - { - case RADIO_CHECK: - case RADIO_CHECK_ACK: - case RADIO_DETACH: - case RADIO_DETACH_ACK: - case RADIO_INHIBIT: - case RADIO_INHIBIT_ACK: - case RADIO_UNINHIBIT: - case RADIO_UNINHIBIT_ACK: - return getArgument(); - default: - break; - } - - return null; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java deleted file mode 100644 index 71257357..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupAffiliationQuery extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( GroupAffiliationQuery.class ); - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public GroupAffiliationQuery( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_AFFILIATION_QUERY.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java deleted file mode 100644 index 0f2580b6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java +++ /dev/null @@ -1,189 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class GroupVoiceChannelUpdate extends LDU1Message implements IdentifierReceiver -{ - public static final int[] IDENTIFIER_A = {364, 365, 366, 367}; - public static final int[] CHANNEL_A = {372, 373, 374, 375, 376, 377, 382, 383, 384, 385, 386, 387}; - public static final int[] GROUP_ADDRESS_A = {536, 537, 538, 539, 540, 541, 546, 547, 548, 549, 550, 551, 556, 557, - 558, 559}; - public static final int[] IDENTIFIER_B = {560, 561, 566, 567}; - public static final int[] CHANNEL_B = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; - public static final int[] GROUP_ADDRESS_B = {732, 733, 734, 735, 740, 741, 742, 743, 744, 745, 750, 751, 752, 753, - 754, 755}; - - private IBandIdentifier mIdentifierUpdateA; - private IBandIdentifier mIdentifierUpdateB; - - public GroupVoiceChannelUpdate(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" CHAN A:" + getChannelA()); - - sb.append(" GRP A:" + getGroupAddressA()); - - if(hasChannelB()) - { - sb.append(" CHAN B:" + getChannelA()); - - sb.append(" GRP B:" + getGroupAddressA()); - } - - return sb.toString(); - } - - public int getChannelIdentifierA() - { - return mMessage.getInt(IDENTIFIER_A); - } - - public int getChannelNumberA() - { - return mMessage.getInt(CHANNEL_A); - } - - public String getChannelA() - { - return getChannelIdentifierA() + "-" + getChannelNumberA(); - } - - public int getChannelIdentifierB() - { - return mMessage.getInt(IDENTIFIER_B); - } - - public int getChannelNumberB() - { - return mMessage.getInt(CHANNEL_B); - } - - public String getChannelB() - { - return getChannelIdentifierB() + "-" + getChannelNumberB(); - } - - public boolean hasChannelB() - { - return getChannelNumberB() != 0; - } - - public String getGroupAddressA() - { - return mMessage.getHex(GROUP_ADDRESS_A, 4); - } - - public String getGroupAddressB() - { - return mMessage.getHex(GROUP_ADDRESS_B, 4); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getChannelIdentifierA()) - { - mIdentifierUpdateA = message; - } - if(identifier == getChannelIdentifierB()) - { - mIdentifierUpdateB = message; - } - } - - @Override - public int[] getIdentifiers() - { - if(hasChannelB()) - { - int[] identifiers = new int[2]; - identifiers[0] = getChannelIdentifierA(); - identifiers[1] = getChannelIdentifierB(); - - return identifiers; - } - else - { - int[] identifiers = new int[1]; - identifiers[0] = getChannelIdentifierA(); - - return identifiers; - } - } - - public long getDownlinkFrequencyA() - { - if(mIdentifierUpdateA != null) - { - return mIdentifierUpdateA.getDownlinkFrequency(getChannelNumberA()); - } - - return 0; - } - - public long getUplinkFrequencyA() - { - if(mIdentifierUpdateA != null) - { - return mIdentifierUpdateA.getUplinkFrequency(getChannelNumberA()); - } - - return 0; - } - - public long getDownlinkFrequencyB() - { - if(mIdentifierUpdateB != null) - { - return mIdentifierUpdateB.getDownlinkFrequency(getChannelNumberB()); - } - - return 0; - } - - public long getUplinkFrequencyB() - { - if(mIdentifierUpdateB != null) - { - return mIdentifierUpdateB.getUplinkFrequency(getChannelNumberB()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java deleted file mode 100644 index 91cc6eaa..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class GroupVoiceChannelUpdateExplicit extends LDU1Message implements IdentifierReceiver -{ - public static final int EMERGENCY_FLAG = 376; - public static final int ENCRYPTED_CHANNEL_FLAG = 377; - public static final int DUPLEX_MODE = 382; - public static final int SESSION_MODE = 383; - public static final int[] GROUP_ADDRESS = {536, 537, 538, 539, 540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567}; - public static final int[] TRANSMIT_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; - public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735}; - public static final int[] RECEIVE_CHANNEL = {740, 741, 742, 743, 744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupVoiceChannelUpdateExplicit(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - if(isEncryptedChannel()) - { - sb.append(" ENCRYPTED CHANNEL"); - } - - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncryptedChannel() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getGroupAddress() - { - return mMessage.getHex(GROUP_ADDRESS, 4); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitChannelIdentifier(); - identifiers[1] = getReceiveChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java deleted file mode 100644 index 91a3d306..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupVoiceChannelUser extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( GroupVoiceChannelUser.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 376; - public static final int ENCRYPTED_CHANNEL_FLAG = 377; - public static final int DUPLEX_MODE = 378; - public static final int SESSION_MODE = 379; - public static final int[] GROUP_ADDRESS = { 548,549,550,551,556,557,558,559, - 560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public GroupVoiceChannelUser( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_VOICE_CHANNEL_USER.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getGroupAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java deleted file mode 100644 index de4ef637..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.Vendor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class LDULCMessageFactory -{ - private final static Logger mLog = - LoggerFactory.getLogger( LDULCMessageFactory.class ); - - /** - * Constructs a new sub-class of the TDULC message if the opcode is recognized - * or returns the original message. - */ - public static LDU1Message getMessage(LDU1Message message ) - { - if( message.isImplicitFormat() || message.getVendor() == Vendor.STANDARD ) - { - switch( message.getOpcode() ) - { - case ADJACENT_SITE_STATUS_BROADCAST: - return new AdjacentSiteStatusBroadcast( message ); - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - return new AdjacentSiteStatusBroadcastExplicit( message ); - case CALL_ALERT: - return new CallAlert( message ); - case CALL_TERMINATION_OR_CANCELLATION: - return new CallTermination( message ); - case CHANNEL_IDENTIFIER_UPDATE: - return new ChannelIdentifierUpdate( message ); - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - return new ChannelIdentifierUpdateExplicit( message ); - case EXTENDED_FUNCTION_COMMAND: - return new ExtendedFunctionCommand( message ); - case GROUP_AFFILIATION_QUERY: - return new GroupAffiliationQuery( message ); - case GROUP_VOICE_CHANNEL_UPDATE: - return new GroupVoiceChannelUpdate( message ); - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - return new GroupVoiceChannelUpdateExplicit( message ); - case GROUP_VOICE_CHANNEL_USER: - return new GroupVoiceChannelUser( message ); - case MESSAGE_UPDATE: - return new MessageUpdate( message ); - case NETWORK_STATUS_BROADCAST: - return new NetworkStatusBroadcast( message ); - case NETWORK_STATUS_BROADCAST_EXPLICIT: - return new NetworkStatusBroadcastExplicit( message ); - case PROTECTION_PARAMETER_BROADCAST: - return new ProtectionParameterBroadcast( message ); - case RFSS_STATUS_BROADCAST: - return new RFSSStatusBroadcast( message ); - case RFSS_STATUS_BROADCAST_EXPLICIT: - return new RFSSStatusBroadcastExplicit( message ); - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - return new SecondaryControlChannelBroadcast( message ); - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - return new SecondaryControlChannelBroadcastExplicit( message ); - case STATUS_QUERY: - return new StatusQuery( message ); - case STATUS_UPDATE: - return new StatusUpdate( message ); - case SYSTEM_SERVICE_BROADCAST: - return new SystemServiceBroadcast( message ); - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - return new TelephoneInterconnectAnswerRequest( message ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - return new TelephoneInterconnectVoiceChannelUser( message ); - case UNIT_AUTHENTICATION_COMMAND: - return new UnitAuthenticationCommand( message ); - case UNIT_REGISTRATION_COMMAND: - return new UnitRegistrationCommand( message ); - case UNIT_TO_UNIT_ANSWER_REQUEST: - return new UnitToUnitAnswerRequest( message ); - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - return new UnitToUnitVoiceChannelUser( message ); - default: - break; - } - } - else - { - switch( message.getVendor() ) - { - default: - break; - } - } - - return message; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/MessageUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/MessageUpdate.java deleted file mode 100644 index f4668f99..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/MessageUpdate.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageUpdate extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( MessageUpdate.class ); - public static final int[] SHORT_DATA_MESSAGE = { 364,365,366,367,372,373, - 374,375,376,377,382,383,384,385,386,387 }; - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public MessageUpdate( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.MESSAGE_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " MSG:" + getShortDataMessage() ); - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getShortDataMessage() - { - return mMessage.getHex( SHORT_DATA_MESSAGE, 4 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java deleted file mode 100644 index 528439e5..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Service; - -public class NetworkStatusBroadcast extends LDU1Message implements IdentifierReceiver -{ - public static final int[] NETWORK_ID = {376, 377, 382, 383, 384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, - 547, 548, 549, 550, 551}; - public static final int[] SYSTEM_ID = {556, 557, 558, 559, 560, 561, 566, 567, 568, 569, 570, 571}; - public static final int[] IDENTIFIER = {720, 721, 722, 723}; - public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; - public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mIdentifierUpdate; - - public NetworkStatusBroadcast(LDU1Message source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.NETWORK_STATUS_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" NETWORK:" + getNetworkID()); - sb.append(" SYS:" + getSystem()); - sb.append(" CHAN:" + getChannel()); - sb.append(" " + Service.getServices(getSystemServiceClass()).toString()); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex(NETWORK_ID, 5); - } - - public String getSystem() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java deleted file mode 100644 index b51bf9d0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java +++ /dev/null @@ -1,148 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class NetworkStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver -{ - public static final int[] NETWORK_ID = {364, 365, 366, 367, 372, 373, 374, 375, 376, 377, 382, 383, 384, 385, 386, - 387, 536, 537, 538, 539}; - public static final int[] SYSTEM_ID = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567}; - public static final int[] TRANSMIT_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; - public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735}; - public static final int[] RECEIVE_CHANNEL = {740, 741, 742, 743, 744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public NetworkStatusBroadcastExplicit(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.NETWORK_STATUS_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" NETWORK:" + getNetworkID()); - sb.append(" SYS:" + getSystem()); - sb.append(" TRANSMIT:" + getTransmitChannel()); - sb.append(" RECEIVE:" + getReceiveChannel()); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex(NETWORK_ID, 5); - } - - public String getSystem() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java deleted file mode 100644 index 3bb551d0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.Encryption; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ProtectionParameterBroadcast extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( ProtectionParameterBroadcast.class ); - - public static final int[] ALGORITHM_ID = { 536,537,538,539,540,541,546,547 }; - public static final int[] KEY_ID = { 548,549,550,551,556,557,558,559,560, - 561,566,567,568,569,570,571 }; - public static final int[] TARGET_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public ProtectionParameterBroadcast( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.PROTECTION_PARAMETER_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ENCRYPTION:" + getEncryption().name() ); - sb.append( " KEY:" + getEncryptionKey() ); - sb.append( " ADDRESSS:" + getTargetAddress() ); - - return sb.toString(); - } - - public Encryption getEncryption() - { - return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); - } - - public int getEncryptionKey() - { - return mMessage.getInt( KEY_ID ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java deleted file mode 100644 index bae98a54..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class RFSSStatusBroadcast extends LDU1Message implements IdentifierReceiver -{ - public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; - public static final int[] SYSTEM_ID = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; - public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; - public static final int[] IDENTIFIER = {720, 721, 722, 723}; - public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; - public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mIdentifierUpdate; - - public RFSSStatusBroadcast(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.RFSS_STATUS_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" SYS:" + getSystem()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" CHAN:" + getChannelNumber()); - - sb.append(" " + SystemService.toString(getSystemServiceClass())); - - sb.append(" " + mMessage.toString()); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LRA, 2); - } - - public String getSystem() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java deleted file mode 100644 index 17c5a9d6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java +++ /dev/null @@ -1,168 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class RFSSStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver -{ - public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; - public static final int[] RECEIVE_IDENTIFIER = {376, 377, 382, 383}; - public static final int[] RECEIVE_CHANNEL = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; - public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; - public static final int[] TRANSMIT_IDENTIFIER = {720, 721, 722, 723}; - public static final int[] TRANSMIT_CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; - public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public RFSSStatusBroadcastExplicit(LDU1Message source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.RFSS_STATUS_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" TRANSMIT:" + getTransmitChannelNumber()); - - sb.append(" RECEIVE:" + getReceiveChannelNumber()); - - sb.append(" " + SystemService.toString(getSystemServiceClass())); - - sb.append(" " + mMessage.toString()); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LRA, 2); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java deleted file mode 100644 index 6beae5d3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class SecondaryControlChannelBroadcast extends LDU1Message implements IdentifierReceiver -{ - public static final int[] RFSS_ID = {364, 365, 366, 367, 372, 373, 374, 375}; - public static final int[] SITE_ID = {376, 377, 382, 383, 384, 385, 386, 387}; - public static final int[] IDENTIFIER_A = {536, 537, 538, 539}; - public static final int[] CHANNEL_A = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] SYSTEM_SERVICE_CLASS_A = {560, 561, 566, 567, 568, 569, 570, 571}; - public static final int[] IDENTIFIER_B = {720, 721, 722, 723}; - public static final int[] CHANNEL_B = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; - public static final int[] SYSTEM_SERVICE_CLASS_B = {744, 745, 750, 751, 752, 753, 754, 755}; - - private IBandIdentifier mIdentifierProviderA; - private IBandIdentifier mIdentifierProviderB; - - public SecondaryControlChannelBroadcast(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" CHAN A:" + getChannelA()); - - sb.append(" " + SystemService.toString(getSystemServiceClassA())); - - sb.append(" CHAN B:" + getChannelB()); - - sb.append(" " + SystemService.toString(getSystemServiceClassB())); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifierA() - { - return mMessage.getInt(IDENTIFIER_A); - } - - public int getChannelA() - { - return mMessage.getInt(CHANNEL_A); - } - - public String getChannelNumberA() - { - return getIdentifierA() + "-" + getChannelA(); - } - - public int getIdentifierB() - { - return mMessage.getInt(IDENTIFIER_B); - } - - public int getChannelB() - { - return mMessage.getInt(CHANNEL_B); - } - - public String getChannelNumberB() - { - return getIdentifierB() + "-" + getChannelB(); - } - - public int getSystemServiceClassA() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS_A); - } - - public int getSystemServiceClassB() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS_B); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getIdentifierA()) - { - mIdentifierProviderA = message; - } - if(identifier == getIdentifierB()) - { - mIdentifierProviderB = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getIdentifierA(); - identifiers[1] = getIdentifierB(); - - return identifiers; - } - - public long getDownlinkFrequencyA() - { - if(mIdentifierProviderA != null) - { - return mIdentifierProviderA.getDownlinkFrequency(getChannelA()); - } - - return 0; - } - - public long getUplinkFrequencyA() - { - if(mIdentifierProviderA != null) - { - return mIdentifierProviderA.getUplinkFrequency(getChannelA()); - } - - return 0; - } - - public long getDownlinkFrequencyB() - { - if(mIdentifierProviderB != null) - { - return mIdentifierProviderB.getDownlinkFrequency(getChannelB()); - } - - return 0; - } - - public long getUplinkFrequencyB() - { - if(mIdentifierProviderB != null) - { - return mIdentifierProviderB.getUplinkFrequency(getChannelB()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java deleted file mode 100644 index 408d651c..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class SecondaryControlChannelBroadcastExplicit extends LDU1Message implements IdentifierReceiver -{ - public static final int[] RFSS_ID = {364, 365, 366, 367, 372, 373, 374, 375}; - public static final int[] SITE_ID = {376, 377, 382, 383, 384, 385, 386, 387}; - public static final int[] TRANSMIT_IDENTIFIER = {536, 537, 538, 539}; - public static final int[] TRANSMIT_CHANNEL = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; - public static final int[] RECEIVE_IDENTIFIER = {560, 561, 566, 567}; - public static final int[] RECEIVE_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; - public static final int[] SYSTEM_SERVICE_CLASS = {732, 733, 734, 735, 740, 741, 742, 743}; - - private IBandIdentifier mTransmitIdentifierProvider; - private IBandIdentifier mReceiveIdentifierProvider; - - public SecondaryControlChannelBroadcastExplicit(LDU1Message message) - { - super(message); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" TRANSMIT:" + getTransmitChannel()); - - sb.append(" RECEIVE:" + getReceiveChannel()); - - sb.append(" " + SystemService.toString(getSystemServiceClass())); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierProvider = message; - } - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierProvider = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierProvider != null) - { - return mTransmitIdentifierProvider.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierProvider != null) - { - return mReceiveIdentifierProvider.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusQuery.java deleted file mode 100644 index 7db8e146..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusQuery.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class StatusQuery extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( StatusQuery.class ); - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public StatusQuery( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.STATUS_QUERY.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusUpdate.java deleted file mode 100644 index 5d7c878c..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/StatusUpdate.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class StatusUpdate extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( StatusUpdate.class ); - public static final int[] USER_STATUS = { 364,365,366,367,372,373,374,375 }; - public static final int[] UNIT_STATUS = { 376,377,382,383,384,385,386,387 }; - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public StatusUpdate( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.STATUS_UPDATE.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " STATUS USER:" + getUserStatus() ); - sb.append( " UNIT:" + getUnitStatus() ); - sb.append( " FROM:" + getSourceAddress() ); - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getUserStatus() - { - return mMessage.getHex( USER_STATUS, 2 ); - } - - public String getUnitStatus() - { - return mMessage.getHex( UNIT_STATUS, 2 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java deleted file mode 100644 index de530908..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Service; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -public class SystemServiceBroadcast extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( SystemServiceBroadcast.class ); - public static final int[] REQUEST_PRIORITY_LEVEL = { 384,385,386,387 }; - public static final int[] AVAILABLE_SERVICES = { 536,537,538,539,540,541, - 546,547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SUPPORTED_SERVICES = { 720,721,722,723,724,725, - 730,731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public SystemServiceBroadcast( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.SYSTEM_SERVICE_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SERVICES AVAILABLE " ); - - sb.append( getAvailableServices() ); - - sb.append( " SUPPORTED " ); - - sb.append( getSupportedServices() ); - - return sb.toString(); - } - - public List getAvailableServices() - { - long bitmap = mMessage.getLong( AVAILABLE_SERVICES ); - - return Service.getServices( bitmap ); - } - - public List getSupportedServices() - { - long bitmap = mMessage.getLong( SUPPORTED_SERVICES ); - - return Service.getServices( bitmap ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java deleted file mode 100644 index 776f8ba6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.Digit; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -public class TelephoneInterconnectAnswerRequest extends LDU1Message -{ - private final static Logger mLog = LoggerFactory.getLogger( - TelephoneInterconnectAnswerRequest.class ); - - public static final int[] DIGIT_1 = { 364,365,366,367 }; - public static final int[] DIGIT_2 = { 372,373,374,375 }; - public static final int[] DIGIT_3 = { 376,377,382,383 }; - public static final int[] DIGIT_4 = { 384,385,386,387 }; - public static final int[] DIGIT_5 = { 536,537,538,539 }; - public static final int[] DIGIT_6 = { 540,541,546,547 }; - public static final int[] DIGIT_7 = { 548,549,550,551 }; - public static final int[] DIGIT_8 = { 556,557,558,559 }; - public static final int[] DIGIT_9 = { 560,561,566,567 }; - public static final int[] DIGIT_10 = { 568,569,570,571 }; - public static final int[] TARGET_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public TelephoneInterconnectAnswerRequest( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.TELEPHONE_INTERCONNECT_ANSWER_REQUEST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " TEL:" ); - sb.append( getTelephoneNumber() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getTelephoneNumber() - { - List digits = new ArrayList(); - - digits.add( mMessage.getInt( DIGIT_1 ) ); - digits.add( mMessage.getInt( DIGIT_2 ) ); - digits.add( mMessage.getInt( DIGIT_3 ) ); - digits.add( mMessage.getInt( DIGIT_4 ) ); - digits.add( mMessage.getInt( DIGIT_5 ) ); - digits.add( mMessage.getInt( DIGIT_6 ) ); - digits.add( mMessage.getInt( DIGIT_7 ) ); - digits.add( mMessage.getInt( DIGIT_8 ) ); - digits.add( mMessage.getInt( DIGIT_9 ) ); - digits.add( mMessage.getInt( DIGIT_10 ) ); - - return Digit.decode( digits ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java deleted file mode 100644 index 9753c197..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TelephoneInterconnectVoiceChannelUser extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( TelephoneInterconnectVoiceChannelUser.class ); - - /* Service Options */ - public static final int EMERGENCY_FLAG = 376; - public static final int ENCRYPTED_CHANNEL_FLAG = 377; - public static final int DUPLEX_MODE = 382; - public static final int SESSION_MODE = 383; - public static final int[] CALL_TIMER = { 548,549,550,551,556,557,558,559, - 560,561,566,567,568,569,570,571 }; - public static final int[] ADDRESS = { 720,721,722,723,724,725,730,731,732, - 733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public TelephoneInterconnectVoiceChannelUser( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " TIMER:" + getCallTimer() + " SECS" ); - - sb.append( " ADDR:" + getAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getAddress() - { - return mMessage.getHex( ADDRESS, 6 ); - } - - /** - * Call timer in seconds - */ - public int getCallTimer() - { - int units = mMessage.getInt( CALL_TIMER ); - - return (int)( units / 10 ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java deleted file mode 100644 index cfd952f3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitAuthenticationCommand extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitAuthenticationCommand.class ); - public static final int[] NETWORK_ID = { 364,365,366,367,372,373,374,375, - 376,377,382,383,384,385,386,387,536,537,538,539 }; - public static final int[] SYSTEM_ID = { 540,541,546,547,548,549,550,551,556, - 557,558,559 }; - public static final int[] TARGET_ID = { 560,561,566,567,568,569,570,571,720, - 721,722,723,724,725,730,731,732,733,734,735,740,741,742,743 }; - - public UnitAuthenticationCommand( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_AUTHENTICATION_COMMAND.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ADDRESS:" + getCompleteTargetAddress() ); - - return sb.toString(); - } - - public String getCompleteTargetAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getNetworkID() ); - sb.append( ":" ); - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getTargetID() ); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex( NETWORK_ID, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getTargetID() - { - return mMessage.getHex( TARGET_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java deleted file mode 100644 index 1ee90d23..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitRegistrationCommand extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitRegistrationCommand.class ); - public static final int[] NETWORK_ID = { 364,365,366,367,372,373,374,375, - 376,377,382,383,384,385,386,387,536,537,538,539 }; - public static final int[] SYSTEM_ID = { 540,541,546,547,548,549,550,551,556, - 557,558,559 }; - public static final int[] TARGET_ID = { 560,561,566,567,568,569,570,571,720, - 721,722,723,724,725,730,731,732,733,734,735,740,741,742,743 }; - - public UnitRegistrationCommand( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_REGISTRATION_COMMAND.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ADDRESS:" + getCompleteTargetAddress() ); - - return sb.toString(); - } - - public String getCompleteTargetAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getNetworkID() ); - sb.append( ":" ); - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getTargetID() ); - - return sb.toString(); - } - - - public String getNetworkID() - { - return mMessage.getHex( NETWORK_ID, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getTargetID() - { - return mMessage.getHex( TARGET_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java deleted file mode 100644 index 45a1fdbc..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitToUnitAnswerRequest extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitToUnitAnswerRequest.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 364; - public static final int ENCRYPTED_CHANNEL_FLAG = 365; - public static final int DUPLEX_MODE = 366; - public static final int SESSION_MODE = 367; - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public UnitToUnitAnswerRequest( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_TO_UNIT_ANSWER_REQUEST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public P25Message.DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? P25Message.DuplexMode.FULL : P25Message.DuplexMode.HALF; - } - - public P25Message.SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - P25Message.SessionMode.CIRCUIT : P25Message.SessionMode.PACKET; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java deleted file mode 100644 index ab26df62..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitToUnitVoiceChannelUser extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitToUnitVoiceChannelUser.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 376; - public static final int ENCRYPTED_CHANNEL_FLAG = 377; - public static final int DUPLEX_MODE = 382; - public static final int SESSION_MODE = 383; - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public UnitToUnitVoiceChannelUser( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_TO_UNIT_VOICE_CHANNEL_USER.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java deleted file mode 100644 index add7846d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.ldu.lc.motorola; - -import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MotorolaOpcode_0 extends LDU1Message -{ - private final static Logger mLog = - LoggerFactory.getLogger( MotorolaOpcode_0.class ); - public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, - 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; - - public MotorolaOpcode_0( LDU1Message message ) - { - super( message ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.STATUS_QUERY.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeader.java new file mode 100644 index 00000000..40316f25 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeader.java @@ -0,0 +1,193 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.IBitErrorProvider; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.PDUFormat; +import io.github.dsheirer.module.decode.p25.reference.Vendor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * P25 Packet Data Unit header + */ +public class PDUHeader implements IBitErrorProvider +{ + private final static Logger mLog = LoggerFactory.getLogger(PDUHeader.class); + + public static final int CONFIRMATION_REQUIRED_INDICATOR = 1; + public static final int PACKET_DIRECTION_INDICATOR = 2; + public static final int[] PDU_FORMAT = {3, 4, 5, 6, 7}; + public static final int[] VENDOR_ID = {16, 17, 18, 19, 20, 21, 22, 23}; + public static final int[] LOGICAL_LINK_ID = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + public static final int[] BLOCKS_TO_FOLLOW = {49, 50, 51, 52, 53, 54, 55}; + public static final int[] PDU_CRC = {80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + + protected boolean mValid; + private CorrectedBinaryMessage mMessage; + private Identifier mLLID; + + /** + * Constructs a PDU header. + * + * @param message + * @param passesCRC + */ + public PDUHeader(CorrectedBinaryMessage message, boolean passesCRC) + { + mMessage = message; + mValid = passesCRC; + } + + public CorrectedBinaryMessage getMessage() + { + return mMessage; + } + + /** + * Indicates if this header was correctly decoded and passed CCITT-16 CRC error check. + */ + public boolean isValid() + { + return mValid; + } + + /** + * Indicates if this PDU requires confirmation of receipt + */ + public boolean isConfirmationRequired() + { + return getMessage().get(CONFIRMATION_REQUIRED_INDICATOR); + } + + /** + * Direction of this message, inbound or outbound. + */ + public Direction getDirection() + { + return Direction.fromValue(getMessage().get(PACKET_DIRECTION_INDICATOR)); + } + + /** + * Indicates if this is an outbound (true) FNE -> SU, or an inbound (false) SU -> FNE packet. + */ + public boolean isOutbound() + { + return getDirection() == Direction.OUTBOUND; + } + + /** + * Packet Data Unit format + */ + public PDUFormat getFormat() + { + return getFormat(getMessage()); + } + + /** + * Determines the PDU format for the message + */ + public static PDUFormat getFormat(BinaryMessage binaryMessage) + { + return PDUFormat.fromValue(binaryMessage.getInt(PDU_FORMAT)); + } + + /** + * Number of bits processed to produce this header. + * + * @return 196 bits + */ + @Override + public int getBitsProcessedCount() + { + return 196; + } + + /** + * Number of bit errors detected and/or corrected + */ + @Override + public int getBitErrorsCount() + { + return getMessage().getCorrectedBitCount(); + } + + /** + * Vendor or manufacturer ID + */ + public Vendor getVendor() + { + return Vendor.fromValue(getMessage().getInt(VENDOR_ID)); + } + + /** + * Logical Link Identifier (ie TO radio identifier) + */ + public Identifier getLLID() + { + if(mLLID == null) + { + if(isOutbound()) + { + mLLID = APCO25ToTalkgroup.createIndividual(getMessage().getInt(LOGICAL_LINK_ID)); + } + else + { + mLLID = APCO25FromTalkgroup.createIndividual(getMessage().getInt(LOGICAL_LINK_ID)); + } + } + + return mLLID; + } + + /** + * Number of data blocks that follow this header + */ + public int getBlocksToFollowCount() + { + return getMessage().getInt(BLOCKS_TO_FOLLOW); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("***CRC-FAIL*** "); + } + + sb.append("PDU HEADER FORMAT:"); + sb.append(getFormat().getLabel()); + sb.append(isConfirmationRequired() ? " CONFIRMED" : " UNCONFIRMED"); + sb.append(" VENDOR:" + getVendor().getLabel()); + sb.append(isOutbound() ? "TO" : "FROM").append(" LLID").append(getLLID()); + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeaderFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeaderFactory.java new file mode 100644 index 00000000..0d1379b5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUHeaderFactory.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRCP25; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCHeader; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.PacketHeader; +import io.github.dsheirer.module.decode.p25.message.pdu.response.ResponseHeader; +import io.github.dsheirer.module.decode.p25.reference.PDUFormat; + +public class PDUHeaderFactory +{ + public static PDUHeader getPDUHeader(CorrectedBinaryMessage correctedBinaryMessage) + { + //CCITT-16 can detect and correct up to 1 bit error max - 2 bit errors indicates CRC-fail + int errorCount = CRCP25.correctCCITT80(correctedBinaryMessage, 0, 80); + boolean passesCRC = errorCount < 2; + + correctedBinaryMessage.incrementCorrectedBitCount(errorCount); + + PDUFormat format = PDUHeader.getFormat(correctedBinaryMessage); + + switch(format) + { + case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: + return new AMBTCHeader(correctedBinaryMessage, passesCRC); + case PACKET_DATA: + return new PacketHeader(correctedBinaryMessage, passesCRC); + case RESPONSE_PACKET_HEADER_FORMAT: + return new ResponseHeader(correctedBinaryMessage, passesCRC); + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: + default: + return new PDUHeader(correctedBinaryMessage, passesCRC); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessage.java index 834cd96c..3089f718 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessage.java @@ -1,181 +1,216 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ package io.github.dsheirer.module.decode.p25.message.pdu; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.vendor.VendorOpcode; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; import io.github.dsheirer.module.decode.p25.reference.PDUFormat; import io.github.dsheirer.module.decode.p25.reference.ServiceAccessPoint; import io.github.dsheirer.module.decode.p25.reference.Vendor; +import java.util.Collections; +import java.util.List; + public class PDUMessage extends P25Message { - public static final int CONFIRMATION_REQUIRED_INDICATOR = 65; - public static final int PACKET_DIRECTION_INDICATOR = 66; - public static final int[] FORMAT = { 67,68,69,70,71 }; - public static final int[] SAP_ID = { 74,75,76,77,78,79 }; - public static final int[] VENDOR_ID = { 80,81,82,83,84,85,86,87 }; - public static final int[] LOGICAL_LINK_ID = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] BLOCKS_TO_FOLLOW = { 113,114,115,116,117,118,119 }; - public static final int[] PAD_OCTET_COUNT = { 123,124,125,126,127 }; - public static final int[] OPCODE = { 122,123,124,125,126,127 }; - public static final int[] DATA_HEADER_OFFSET = { 138,139,140,141,142,143 }; - public static final int[] PDU_CRC = { 144,145,146,147,148,149,150,151,152, - 153,154,155,156,157,158,159 }; + public static final int CONFIRMATION_REQUIRED_INDICATOR = 65; + public static final int PACKET_DIRECTION_INDICATOR = 66; + public static final int[] FORMAT = {67, 68, 69, 70, 71}; + public static final int[] SAP_ID = {74, 75, 76, 77, 78, 79}; + public static final int[] VENDOR_ID = {80, 81, 82, 83, 84, 85, 86, 87}; + public static final int[] LOGICAL_LINK_ID = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] BLOCKS_TO_FOLLOW = {113, 114, 115, 116, 117, 118, 119}; + public static final int[] PAD_OCTET_COUNT = {123, 124, 125, 126, 127}; + public static final int[] OPCODE = {122, 123, 124, 125, 126, 127}; + public static final int[] DATA_HEADER_OFFSET = {138, 139, 140, 141, 142, 143}; + public static final int[] PDU_CRC = {144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159}; - public PDUMessage(BinaryMessage message, DataUnitID duid, AliasList aliasList ) + private Identifier mLLID; + + public PDUMessage(CorrectedBinaryMessage message, int nac, long timestamp) { - super( message, duid, aliasList ); + super(message, nac, timestamp); /* Setup a CRC array to hold the header CRC and the multi-block CRC */ - mCRC = new CRC[ 2 ]; - mCRC[ 0 ] = CRC.PASSED; +// mCRC = new CRC[ 2 ]; +// mCRC[ 0 ] = CRC.PASSED; } - - protected String getMessageStub() - { - StringBuilder sb = new StringBuilder(); - Vendor vendor = getVendor(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - - switch( getFormat() ) - { - case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: - sb.append( "ATSBK" ); - break; - case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: - sb.append( "**** UNCONFIRMED MULTI-BLOCK TRUNKING CONTROL" ); - break; - default: - sb.append( getDUID().getLabel() ); - break; - } - - if( vendor == Vendor.STANDARD ) - { - sb.append( " " ); - sb.append( getOpcode().getLabel() ); - } - else - { - sb.append( " " ); - sb.append( vendor.getLabel() ); - } - - return sb.toString(); - } - - @Override - public String getMessage() + @Override + public DataUnitID getDUID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - switch( getFormat() ) - { - case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: - break; - case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: - sb.append( " PAD OCTETS:" + getPadOctetCount() ); - sb.append( " DATA HDR OFFSET:" + getDataHeaderOffset() ); - break; - default: - } - - sb.append( " " ); - sb.append( getConfirmation() ); - sb.append( " " ); - sb.append( getDirection() ); - sb.append( " FMT:" ); - sb.append( getFormat().getLabel() ); - sb.append( " SAP:" ); - sb.append( getServiceAccessPoint().name() ); - sb.append( " VEND:" ); - sb.append( getVendor().getLabel() ); - sb.append( " LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " BLKS TO FOLLOW:" ); - sb.append( getBlocksToFollowCount() ); - - return sb.toString(); + return DataUnitID.PACKET_DATA_UNIT; } - public String getConfirmation() - { - return mMessage.get( CONFIRMATION_REQUIRED_INDICATOR ) ? "CONFIRMED" : "UNCONFIRMED"; - } - - public String getDirection() - { - return mMessage.get( PACKET_DIRECTION_INDICATOR ) ? "OSP" : "ISP"; - } - - public boolean isOutbound() - { - return mMessage.get( PACKET_DIRECTION_INDICATOR ); - } - - public PDUFormat getFormat() - { - return PDUFormat.fromValue( mMessage.getInt( FORMAT ) ); - } - - public ServiceAccessPoint getServiceAccessPoint() - { - return ServiceAccessPoint.fromValue( mMessage.getInt( SAP_ID ) ); - } + protected String getMessageStub() + { + StringBuilder sb = new StringBuilder(); - public Vendor getVendor() - { - return Vendor.fromValue( mMessage.getInt( VENDOR_ID ) ); - } - - public String getLogicalLinkID() - { - return mMessage.getHex( LOGICAL_LINK_ID, 6 ); - } - - public int getBlocksToFollowCount() - { - return mMessage.getInt( BLOCKS_TO_FOLLOW ); - } - - public int getPadOctetCount() - { - return mMessage.getInt( PAD_OCTET_COUNT ); - } - - public Opcode getOpcode() - { - if( getFormat() == PDUFormat.ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL ) - { - return Opcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - return Opcode.UNKNOWN; - } - - public VendorOpcode getVendorOpcode() - { - if( getFormat() == PDUFormat.ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL ) - { - return VendorOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - return VendorOpcode.UNKNOWN; - } - - public int getDataHeaderOffset() - { - return mMessage.getInt( DATA_HEADER_OFFSET ); - } + Vendor vendor = getVendor(); + + sb.append("NAC:"); + sb.append(getNAC()); + sb.append(" "); + + switch(getFormat()) + { + case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: + sb.append("ATSBK"); + break; + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: + sb.append("**** UNCONFIRMED MULTI-BLOCK TRUNKING CONTROL"); + break; + default: + sb.append(getDUID().getLabel()); + break; + } + + if(vendor == Vendor.STANDARD) + { + sb.append(" "); + sb.append(getOpcode().getLabel()); + } + else + { + sb.append(" "); + sb.append(vendor.getLabel()); + } + + return sb.toString(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + switch(getFormat()) + { + case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: + break; + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: + sb.append(" PAD OCTETS:" + getPadOctetCount()); + sb.append(" DATA HDR OFFSET:" + getDataHeaderOffset()); + break; + default: + } + + sb.append(" "); + sb.append(getConfirmation()); + sb.append(" "); + sb.append(getDirection()); + sb.append(" FMT:"); + sb.append(getFormat().getLabel()); + sb.append(" SAP:"); + sb.append(getServiceAccessPoint().name()); + sb.append(" VEND:"); + sb.append(getVendor().getLabel()); + sb.append(" LLID:"); + sb.append(getLogicalLinkID()); + sb.append(" BLKS TO FOLLOW:"); + sb.append(getBlocksToFollowCount()); + + return sb.toString(); + } + + public String getConfirmation() + { + return getMessage().get(CONFIRMATION_REQUIRED_INDICATOR) ? "CONFIRMED" : "UNCONFIRMED"; + } + + public String getDirection() + { + return getMessage().get(PACKET_DIRECTION_INDICATOR) ? "OSP" : "ISP"; + } + + public boolean isOutbound() + { + return getMessage().get(PACKET_DIRECTION_INDICATOR); + } + + public PDUFormat getFormat() + { + return PDUFormat.fromValue(getMessage().getInt(FORMAT)); + } + + public ServiceAccessPoint getServiceAccessPoint() + { + return ServiceAccessPoint.fromValue(getMessage().getInt(SAP_ID)); + } + + public Vendor getVendor() + { + return Vendor.fromValue(getMessage().getInt(VENDOR_ID)); + } + + public Identifier getLogicalLinkID() + { + if(mLLID == null) + { + if(isOutbound()) + { + mLLID = APCO25ToTalkgroup.createIndividual(getMessage().getInt(LOGICAL_LINK_ID)); + } + else + { + mLLID = APCO25FromTalkgroup.createIndividual(getMessage().getInt(LOGICAL_LINK_ID)); + } + } + + return mLLID; + } + + public int getBlocksToFollowCount() + { + return getMessage().getInt(BLOCKS_TO_FOLLOW); + } + + public int getPadOctetCount() + { + return getMessage().getInt(PAD_OCTET_COUNT); + } + + public Opcode getOpcode() + { + if(getFormat() == PDUFormat.ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL) + { + return Opcode.fromValue(getMessage().getInt(OPCODE)); + } + + return Opcode.OSP_UNKNOWN; + } + + public int getDataHeaderOffset() + { + return getMessage().getInt(DATA_HEADER_OFFSET); + } + + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessageFactory.java index 4cea5c35..60858a7b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUMessageFactory.java @@ -1,131 +1,379 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ package io.github.dsheirer.module.decode.p25.message.pdu; import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.CallAlertExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.StatusQueryExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantUpdateExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.trellis.ViterbiDecoder_1_2_P25; +import io.github.dsheirer.module.decode.p25.P25Interleave; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCHeader; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCAuthenticationQuery; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCAuthenticationResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCCallAlertRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCGroupAffiliationRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCIndividualDataServiceRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCLocationRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCMessageUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCRoamingAddressRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCStatusQueryRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCStatusQueryResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCStatusUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCUnitAcknowledgeResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCUnitToUnitVoiceServiceAnswerResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp.AMBTCUnitToUnitVoiceServiceRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCAdjacentStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCCallAlert; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupAffiliationQuery; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupAffiliationResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCGroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCIndividualDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCMessageUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCProtectionParameterBroadcast; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCRoamingAddressResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCRoamingAddressUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCStatusQuery; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCStatusUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCTelephoneInterconnectChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCTelephoneInterconnectChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitRegistrationResponse; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitToUnitAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitToUnitVoiceServiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCUnitToUnitVoiceServiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.pdu.block.ConfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.block.DataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.PacketHeader; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.PacketMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacketMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.response.ResponseMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; import io.github.dsheirer.module.decode.p25.reference.PDUFormat; -import io.github.dsheirer.module.decode.p25.reference.Vendor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.BitSet; public class PDUMessageFactory { - public static PDUMessage getMessage( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - PDUFormat format = PDUFormat.fromValue( - message.getInt( PDUMessage.FORMAT ) ); + private final static Logger mLog = LoggerFactory.getLogger(PDUMessageFactory.class); - switch( format ) - { - case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: - Vendor vendor = Vendor.fromValue( - message.getInt( PDUMessage.VENDOR_ID ) ); - - switch( vendor ) - { - case STANDARD: - Opcode opcode = Opcode.fromValue( - message.getInt( PDUMessage.OPCODE ) ); - - switch( opcode ) - { - case ADJACENT_STATUS_BROADCAST: - return new AdjacentStatusBroadcastExtended( - message, duid, aliasList ); - case CALL_ALERT: - return new CallAlertExtended( message, duid, - aliasList ); - case GROUP_AFFILIATION_QUERY: - return new GroupAffiliationQueryExtended( - message, duid, aliasList ); - case GROUP_AFFILIATION_RESPONSE: - return new GroupAffiliationResponseExtended( - message, duid, aliasList ); - case GROUP_DATA_CHANNEL_GRANT: - return new GroupDataChannelGrantExtended( - message, duid, aliasList ); - case GROUP_VOICE_CHANNEL_GRANT: - - return new GroupVoiceChannelGrantExplicit( - message, duid, aliasList ); - case INDIVIDUAL_DATA_CHANNEL_GRANT: - return new IndividualDataChannelGrantExtended( - message, duid, aliasList ); - case MESSAGE_UPDATE: - return new MessageUpdateExtended( message, duid, - aliasList ); - case NETWORK_STATUS_BROADCAST: - return new NetworkStatusBroadcastExtended( - message, duid, aliasList ); - case PROTECTION_PARAMETER_BROADCAST: - return new ProtectionParameterBroadcast( - message, duid, aliasList ); - case RFSS_STATUS_BROADCAST: - return new RFSSStatusBroadcastExtended( message, - duid, aliasList ); - case ROAMING_ADDRESS_UPDATE: - return new RoamingAddressUpdateExtended( - message, duid, aliasList ); - case STATUS_QUERY: - return new StatusQueryExtended( message, duid, - aliasList ); - case STATUS_UPDATE: - return new StatusUpdateExtended( message, duid, - aliasList ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - return new TelephoneInterconnectChannelGrantExplicit( - message, duid, aliasList ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - return new TelephoneInterconnectChannelGrantUpdateExplicit( - message, duid, aliasList ); - case UNIT_REGISTRATION_RESPONSE: - return new UnitRegistrationResponseExtended( - message, duid, aliasList ); - case UNIT_TO_UNIT_ANSWER_REQUEST: - return new UnitToUnitAnswerRequestExplicit( - message, duid, aliasList ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - return new UnitToUnitVoiceChannelGrantExtended( - message, duid, aliasList ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - return new UnitToUnitVoiceChannelGrantUpdateExtended( - message, duid, aliasList ); - default: - break; - } - case MOTOROLA: - break; - default: - break; - } - - return new PDUMessage( message, duid, aliasList ); - - case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: - return new PDUMessage( message, duid, aliasList ); - default: - return new PDUMessage( message, duid, aliasList ); - } - } + private static final int[] BLOCK_0_UMBTC_OPCODE = {2, 3, 4, 5, 6, 7}; + private static final int PDU0_BEGIN = 0; + private static final int PDU0_END = 196; + + private static final ViterbiDecoder_1_2_P25 VITERBI_HALF_RATE_DECODER = new ViterbiDecoder_1_2_P25(); + + public static PDUSequence createPacketSequence(int nac, long timestamp, CorrectedBinaryMessage correctedBinaryMessage) + { + //Get deinterleaved header chunk + BitSet interleaved = correctedBinaryMessage.get(PDU0_BEGIN, PDU0_END); + CorrectedBinaryMessage deinterleaved = P25Interleave.deinterleaveChunk(P25Interleave.DATA_DEINTERLEAVE, interleaved); + + //Decode 1/2 rate trellis encoded PDU header + CorrectedBinaryMessage viterbiDecoded = VITERBI_HALF_RATE_DECODER.decode(deinterleaved); + + if(viterbiDecoded != null) + { + PDUHeader header = PDUHeaderFactory.getPDUHeader(viterbiDecoded); + return new PDUSequence(header, timestamp, nac); + } + + return null; + } + + /** + * Creates a packet sequence message from the packet sequence. + * + * @param pduSequence + * @return + */ + public static P25Message create(PDUSequence pduSequence, int nac, long timestamp) + { + switch(pduSequence.getHeader().getFormat()) + { + case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: + return createAMBTC(pduSequence, nac, timestamp); + case PACKET_DATA: + return createPacketData(pduSequence, nac, timestamp); + case RESPONSE_PACKET_HEADER_FORMAT: + return new ResponseMessage(pduSequence, nac, timestamp); + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: + return createUMBTC(pduSequence, nac, timestamp); + default: + return new PDUSequenceMessage(pduSequence, nac, timestamp); + } + } + + /** + * Creates a confirmed data block for a packet sequence + */ + public static DataBlock createConfirmedDataBlock(CorrectedBinaryMessage interleaved) + { + CorrectedBinaryMessage deinterleaved = P25Interleave.deinterleaveChunk(P25Interleave.DATA_DEINTERLEAVE, interleaved); + return new ConfirmedDataBlock(deinterleaved); + } + + /** + * Creates an unconfirmed data block for a packet sequence + */ + public static DataBlock createUnconfirmedDataBlock(CorrectedBinaryMessage interleaved) + { + CorrectedBinaryMessage deinterleaved = P25Interleave.deinterleaveChunk(P25Interleave.DATA_DEINTERLEAVE, interleaved); + return new UnconfirmedDataBlock(deinterleaved); + } + + /** + * Creates a packet data message + * + * @param pduSequence containing a packet data header and zero or more data blocks + * @param nac network access code + * @param timestamp of the packet sequence + * @return packet data message parser + */ + public static P25Message createPacketData(PDUSequence pduSequence, int nac, long timestamp) + { + PacketHeader packetHeader = (PacketHeader)pduSequence.getHeader(); + + switch(packetHeader.getServiceAccessPoint()) + { + case UNENCRYPTED_USER_DATA: + return new PacketMessage(pduSequence, nac, timestamp); + case SNDCP_PACKET_DATA_CONTROL: + return new SNDCPPacketMessage(pduSequence, nac, timestamp); + case PACKET_DATA: + return new PacketMessage(pduSequence, nac, timestamp); + case ADDRESS_RESOLUTION_PROTOCOL: + case CHANNEL_REASSIGNMENT: + case CIRCUIT_DATA: + case CIRCUIT_DATA_CONTROL: + case ENCRYPTED_KEY_MANAGEMENT_MESSAGE: + case ENCRYPTED_TRUNKING_CONTROL: + case ENCRYPTED_USER_DATA: + case EXTENDED_ADDRESS: + case MOBILE_RADIO_CONFIGURATION: + case MOBILE_RADIO_LOOPBACK: + case MOBILE_RADIO_PAGING: + case MOBILE_RADIO_STATISTICS: + case MOBILE_RADIO_OUT_OF_SERVICE: + case REGISTRATION_AND_AUTHORIZATION: + case SYSTEM_CONFIGURATION: + case UNENCRYPTED_KEY_MANAGEMENT_MESSAGE: + case UNENCRYPTED_TRUNKING_CONTROL: + case UNKNOWN: + default: + } + + return new PDUSequenceMessage(pduSequence, nac, timestamp); + } + + + /** + * Creates an alternate multi-block trunking control message + * + * @param pduSequence containing an AMBTC (PDU) header + * @param nac network access code + * @param timestamp of the packet sequence + * @return AMBTC message parser for the specific opcode + */ + public static P25Message createAMBTC(PDUSequence pduSequence, int nac, long timestamp) + { + AMBTCHeader ambtcHeader = (AMBTCHeader)pduSequence.getHeader(); + + switch(ambtcHeader.getOpcode()) + { + case ISP_AUTHENTICATION_QUERY_OBSOLETE: + return new AMBTCAuthenticationQuery(pduSequence, nac, timestamp); + case ISP_AUTHENTICATION_RESPONSE: + return new AMBTCAuthenticationResponse(pduSequence, nac, timestamp); + case ISP_CALL_ALERT_REQUEST: + return new AMBTCCallAlertRequest(pduSequence, nac, timestamp); + case ISP_GROUP_AFFILIATION_REQUEST: + return new AMBTCGroupAffiliationRequest(pduSequence, nac, timestamp); + case ISP_INDIVIDUAL_DATA_SERVICE_REQUEST: + return new AMBTCIndividualDataServiceRequest(pduSequence, nac, timestamp); + case ISP_LOCATION_REGISTRATION_REQUEST: + return new AMBTCLocationRegistrationRequest(pduSequence, nac, timestamp); + case ISP_MESSAGE_UPDATE_REQUEST: + return new AMBTCMessageUpdateRequest(pduSequence, nac, timestamp); + case ISP_ROAMING_ADDRESS_REQUEST: + return new AMBTCRoamingAddressRequest(pduSequence, nac, timestamp); + case ISP_STATUS_QUERY_REQUEST: + return new AMBTCStatusQueryRequest(pduSequence, nac, timestamp); + case ISP_STATUS_QUERY_RESPONSE: + return new AMBTCStatusQueryResponse(pduSequence, nac, timestamp); + case ISP_STATUS_UPDATE_REQUEST: + return new AMBTCStatusUpdateRequest(pduSequence, nac, timestamp); + case ISP_UNIT_ACKNOWLEDGE_RESPONSE: + return new AMBTCUnitAcknowledgeResponse(pduSequence, nac, timestamp); + case ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST: + return new AMBTCUnitToUnitVoiceServiceRequest(pduSequence, nac, timestamp); + case ISP_UNIT_TO_UNIT_ANSWER_RESPONSE: + return new AMBTCUnitToUnitVoiceServiceAnswerResponse(pduSequence, nac, timestamp); + + case OSP_ADJACENT_STATUS_BROADCAST: + return new AMBTCAdjacentStatusBroadcast(pduSequence, nac, timestamp); + case OSP_CALL_ALERT: + return new AMBTCCallAlert(pduSequence, nac, timestamp); + case OSP_GROUP_DATA_CHANNEL_GRANT: + return new AMBTCGroupDataChannelGrant(pduSequence, nac, timestamp); + case OSP_GROUP_AFFILIATION_QUERY: + return new AMBTCGroupAffiliationQuery(pduSequence, nac, timestamp); + case OSP_GROUP_AFFILIATION_RESPONSE: + return new AMBTCGroupAffiliationResponse(pduSequence, nac, timestamp); + case OSP_GROUP_VOICE_CHANNEL_GRANT: + return new AMBTCGroupVoiceChannelGrant(pduSequence, nac, timestamp); + case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT: + return new AMBTCIndividualDataChannelGrant(pduSequence, nac, timestamp); + case OSP_MESSAGE_UPDATE: + return new AMBTCMessageUpdate(pduSequence, nac, timestamp); + case OSP_PROTECTION_PARAMETER_BROADCAST: + return new AMBTCProtectionParameterBroadcast(pduSequence, nac, timestamp); + case OSP_ROAMING_ADDRESS_UPDATE: + return new AMBTCRoamingAddressUpdate(pduSequence, nac, timestamp); + case OSP_ROAMING_ADDRESS_COMMAND: + return new AMBTCRoamingAddressResponse(pduSequence, nac, timestamp); + case OSP_STATUS_QUERY: + return new AMBTCStatusQuery(pduSequence, nac, timestamp); + case OSP_STATUS_UPDATE: + return new AMBTCStatusUpdate(pduSequence, nac, timestamp); + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + return new AMBTCTelephoneInterconnectChannelGrant(pduSequence, nac, timestamp); + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + return new AMBTCTelephoneInterconnectChannelGrantUpdate(pduSequence, nac, timestamp); + case OSP_UNIT_REGISTRATION_RESPONSE: + return new AMBTCUnitRegistrationResponse(pduSequence, nac, timestamp); + case OSP_UNIT_TO_UNIT_ANSWER_REQUEST: + return new AMBTCUnitToUnitAnswerRequest(pduSequence, nac, timestamp); + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + return new AMBTCUnitToUnitVoiceServiceChannelGrant(pduSequence, nac, timestamp); + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + return new AMBTCUnitToUnitVoiceServiceChannelGrantUpdate(pduSequence, nac, timestamp); + default: + return new PDUSequenceMessage(pduSequence, nac, timestamp); + } + } + + /** + * Creates an unconfirmed multi-block trunking control message + * + * @param pduSequence containing a UMBTC (PDU) header + * @param nac network access code + * @param timestamp of the packet sequence + * @return UMBTC message parser for the specific opcode + */ + public static P25Message createUMBTC(PDUSequence pduSequence, int nac, long timestamp) + { + Opcode opcode = Opcode.OSP_UNKNOWN; + + if(pduSequence.hasDataBlock(0)) + { + opcode = Opcode.fromValue(pduSequence.getDataBlock(0).getMessage().getInt(BLOCK_0_UMBTC_OPCODE), + pduSequence.getHeader().getDirection(), pduSequence.getHeader().getVendor()); + } + + switch(opcode) + { + case ISP_TELEPHONE_INTERCONNECT_EXPLICIT_DIAL_REQUEST: + return new UMBTCTelephoneInterconnectRequestExplicitDialing(pduSequence, nac, timestamp); + default: + return new PDUSequenceMessage(pduSequence, nac, timestamp); + } + } + + @Deprecated + public static PDUMessage getMessage(BinaryMessage message, DataUnitID duid, AliasList aliasList) + { + PDUFormat format = PDUFormat.fromValue(message.getInt(PDUMessage.FORMAT)); + +// switch(format) +// { +// case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL: +// Vendor vendor = Vendor.fromValue( +// message.getInt(PDUMessage.VENDOR_ID)); +// +// switch(vendor) +// { +// case STANDARD: +// Opcode opcode = Opcode.fromValue(message.getInt(PDUMessage.OPCODE)); +// +// switch(opcode) +// { +// case OSP_ADJACENT_STATUS_BROADCAST: +// return new AdjacentStatusBroadcastExtended(message, duid, aliasList); +// case OSP_CALL_ALERT: +// return new CallAlertExtended(message, duid, aliasList); +// case OSP_GROUP_AFFILIATION_QUERY: +// return new GroupAffiliationQueryExtended(message, duid, aliasList); +// case OSP_GROUP_AFFILIATION_RESPONSE: +// return new GroupAffiliationResponseExtended(message, duid, aliasList); +// case OSP_GROUP_DATA_CHANNEL_GRANT: +// return new GroupDataChannelGrantExtended(message, duid, aliasList); +// case OSP_GROUP_VOICE_CHANNEL_GRANT: +// return new GroupVoiceChannelGrantExplicit(message, duid, aliasList); +// case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT: +// return new IndividualDataChannelGrantExtended(message, duid, aliasList); +// case OSP_MESSAGE_UPDATE: +// return new MessageUpdateExtended(message, duid, aliasList); +// case OSP_NETWORK_STATUS_BROADCAST: +// return new NetworkStatusBroadcastExtended(message, duid, aliasList); +// case OSP_PROTECTION_PARAMETER_BROADCAST: +// return new ProtectionParameterBroadcast(message, duid, aliasList); +// case OSP_RFSS_STATUS_BROADCAST: +// return new RFSSStatusBroadcastExtended(message, +// duid, aliasList); +// case OSP_ROAMING_ADDRESS_UPDATE: +// return new RoamingAddressUpdateExtended(message, duid, aliasList); +// case OSP_STATUS_QUERY: +// return new StatusQueryExtended(message, duid, aliasList); +// case OSP_STATUS_UPDATE: +// return new StatusUpdateExtended(message, duid, +// aliasList); +// case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: +// return new TelephoneInterconnectChannelGrantExplicit(message, duid, aliasList); +// case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: +// return new TelephoneInterconnectChannelGrantUpdateExplicit(message, duid, aliasList); +// case OSP_UNIT_REGISTRATION_RESPONSE: +// return new UnitRegistrationResponseExtended(message, duid, aliasList); +// case OSP_UNIT_TO_UNIT_ANSWER_REQUEST: +// return new UnitToUnitAnswerRequestExplicit(message, duid, aliasList); +// case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: +// return new UnitToUnitVoiceChannelGrantExtended(message, duid, aliasList); +// case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: +// return new UnitToUnitVoiceChannelGrantUpdateExtended(message, duid, aliasList); +// default: +// break; +// } +// case MOTOROLA: +// break; +// default: +// break; +// } +// +// return new PDUMessage(message, duid, aliasList); +// +// case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: +// return new PDUMessage(message, duid, aliasList); +// default: +// return new PDUMessage(message, duid, aliasList); +// } + return null; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequence.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequence.java new file mode 100644 index 00000000..d22b253c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequence.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.message.pdu; + +import io.github.dsheirer.message.IBitErrorProvider; +import io.github.dsheirer.module.decode.p25.message.pdu.block.DataBlock; + +import java.util.ArrayList; +import java.util.List; + +/** + * Packet Data Sequence comprised of a Packet Data Header and zero or more Data Blocks. + */ +public class PDUSequence implements IBitErrorProvider +{ + private long mTimestamp; + private int mNAC; + private PDUHeader mHeader; + private List mDataBlocks = new ArrayList<>(); + + public PDUSequence(PDUHeader pduHeader, long timestamp, int nac) + { + mHeader = pduHeader; + mTimestamp = timestamp; + mNAC = nac; + } + + /** + * Network Access Code (NAC) + */ + public int getNAC() + { + return mNAC; + } + + /** + * Timestamp when the header was transmitted + */ + public long getTimestamp() + { + return mTimestamp; + } + + /** + * Indicates if this message contains all of the data blocks specified by the header + */ + public boolean isComplete() + { + return getHeader().getBlocksToFollowCount() == mDataBlocks.size(); + } + + /** + * Adds the deinterleaved, corrected binary message to this sequence as a datablock. The data + * block is decoded according to the header confirmed/unconfirmed indicator. + * + * @param dataBlock for (un)confirmed data + */ + public void addDataBlock(DataBlock dataBlock) + { + mDataBlocks.add(dataBlock); + } + + public boolean hasDataBlock(int index) + { + return getDataBlock(index) != null; + } + + public DataBlock getDataBlock(int index) + { + if(index < mDataBlocks.size()) + { + return mDataBlocks.get(index); + } + + return null; + } + + /** + * Data blocks that follow the header + */ + public List getDataBlocks() + { + return mDataBlocks; + } + + /** + * Packet Data Unit Header + */ + public PDUHeader getHeader() + { + return mHeader; + } + + @Override + public int getBitsProcessedCount() + { + int processed = getHeader().getBitsProcessedCount(); + + for(DataBlock dataBlock : mDataBlocks) + { + processed += dataBlock.getBitsProcessedCount(); + } + + return processed; + } + + @Override + public int getBitErrorsCount() + { + int errorCount = getHeader().getBitErrorsCount(); + + for(DataBlock dataBlock : mDataBlocks) + { + errorCount += dataBlock.getBitErrorsCount(); + } + + return errorCount; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("NAC:").append(getNAC()); + + if(!isComplete()) + { + sb.append(" *INCOMPLETE - RECEIVED ").append(mDataBlocks.size()).append("/") + .append(getHeader().getBlocksToFollowCount()).append(" DATA BLOCKS"); + } + + sb.append(" ").append(getHeader().toString()); + + sb.append(" DATA BLOCKS:").append(mDataBlocks.size()); + + if(!mDataBlocks.isEmpty()) + { + sb.append(" MSG:"); + + for(DataBlock dataBlock: mDataBlocks) + { + sb.append(dataBlock.getMessage().toHexString()); + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequenceMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequenceMessage.java new file mode 100644 index 00000000..e86b759f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/PDUSequenceMessage.java @@ -0,0 +1,43 @@ +package io.github.dsheirer.module.decode.p25.message.pdu; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +public class PDUSequenceMessage extends P25Message +{ + private PDUSequence mPDUSequence; + + public PDUSequenceMessage(PDUSequence PDUSequence, int nac, long timestamp) + { + super(null, nac, timestamp); + mPDUSequence = PDUSequence; + } + + public PDUSequence getPDUSequence() + { + return mPDUSequence; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getPDUSequence().toString()); + return sb.toString(); + } + + @Override + public DataUnitID getDUID() + { + return DataUnitID.PACKET_DATA_UNIT; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java deleted file mode 100644 index 3c72f0f6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java +++ /dev/null @@ -1,220 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class UnitToUnitChannelGrantExtended extends PDUMessage implements IdentifierReceiver -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 128; - public static final int ENCRYPTED_CHANNEL_FLAG = 129; - public static final int DUPLEX_MODE = 130; - public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SOURCE_WACN = {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, - 175, 176, 177, 178, 179}; - public static final int[] SOURCE_SYSTEM_ID = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; - public static final int[] SOURCE_ID = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, - 207, 208, 209, 210, 211, 212, 213, 214, 215}; - public static final int[] TARGET_ADDRESS = {216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, - 230, 231, 232, 233, 234, 235, 236, 237, 238, 239}; - public static final int[] TRANSMIT_IDENTIFIER = {240, 241, 242, 243}; - public static final int[] TRANSMIT_NUMBER = {244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; - public static final int[] RECEIVE_IDENTIFIER = {256, 257, 258, 259}; - public static final int[] RECEIVE_NUMBER = {260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271}; - public static final int[] MULTIPLE_BLOCK_CRC = {320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, - 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public UnitToUnitChannelGrantExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU2(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" FROM ADDR:"); - sb.append(getSourceAddress()); - - sb.append(" ID:"); - sb.append(getSourceID()); - - sb.append(" TO:"); - sb.append(getTargetAddress()); - - sb.append(" WACN:"); - sb.append(getSourceWACN()); - - sb.append(" SYS:"); - sb.append(getSourceSystemID()); - - sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - sb.append(" " + getDownlinkFrequency()); - - sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - sb.append(" " + getUplinkFrequency()); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncrypted() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getSourceAddress() - { - return mMessage.getHex(SOURCE_ADDRESS, 6); - } - - public String getSourceID() - { - return mMessage.getHex(SOURCE_ID, 6); - } - - public String getSourceWACN() - { - return mMessage.getHex(SOURCE_WACN, 5); - } - - public String getSourceSystemID() - { - return mMessage.getHex(SOURCE_SYSTEM_ID, 3); - } - - public String getTargetAddress() - { - return mMessage.getHex(TARGET_ADDRESS, 6); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitChannelIdentifier(); - idens[1] = getReceiveChannelIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCHeader.java new file mode 100644 index 00000000..3012291e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCHeader.java @@ -0,0 +1,90 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.P25Utils; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUHeader; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.ServiceAccessPoint; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +/** + * Alternage Multi-Block Trunking Control Header + */ +public class AMBTCHeader extends PDUHeader +{ + public static final int[] SAP_ID = {10, 11, 12, 13, 14, 15}; + public static final int[] OPCODE = {58, 59, 60, 61, 62, 63}; + public static final int[] DATA = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + public AMBTCHeader(CorrectedBinaryMessage message, boolean passesCRC) + { + super(message, passesCRC); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("*CRC-FAIL*"); + } + + sb.append("AMBTC"); + + Vendor vendor = getVendor(); + + sb.append(" ").append(getOpcode().getLabel()); + + P25Utils.pad(sb, 22); + + if(vendor != Vendor.STANDARD) + { + sb.append(" VENDOR:").append(getVendor()); + } + + sb.append(" HDR:").append(getMessage().toHexString()); + return sb.toString(); + } + + /** + * Service Access Point (SAP) - determines the network service that will process this packet + */ + public ServiceAccessPoint getServiceAccessPoint() + { + return ServiceAccessPoint.fromValue(getMessage().getInt(SAP_ID)); + } + + public Opcode getOpcode() + { + return Opcode.fromValue(getMessage().getInt(OPCODE), getDirection(), getVendor()); + } + + /** + * 2 x Data Octets at the end of the header. + */ + public int getDataOctets() + { + return getMessage().getInt(DATA); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCMessage.java new file mode 100644 index 00000000..8575c0c4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/AMBTCMessage.java @@ -0,0 +1,190 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.IBitErrorProvider; +import io.github.dsheirer.module.decode.p25.P25Utils; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.block.DataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.List; + +public abstract class AMBTCMessage extends P25Message implements IBitErrorProvider +{ + protected static final int[] HEADER_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47}; + + private PDUSequence mPDUSequence; + + public AMBTCMessage(PDUSequence PDUSequence, int nac, long timestamp) + { + super(nac, timestamp); + mPDUSequence = PDUSequence; + } + + @Override + protected String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.getMessageStub()); + sb.append(" ").append(getHeader().getOpcode()); + P25Utils.pad(sb, 30); + + return sb.toString(); + } + + public AMBTCHeader getHeader() + { + return (AMBTCHeader)getPDUSequence().getHeader(); + } + + public boolean hasDataBlock(int index) + { + return index < getPDUSequence().getDataBlocks().size(); + } + + public UnconfirmedDataBlock getDataBlock(int index) + { + if(index < getPDUSequence().getDataBlocks().size()) + { + DataBlock dataBlock = getPDUSequence().getDataBlocks().get(index); + + if(dataBlock instanceof UnconfirmedDataBlock) + { + return (UnconfirmedDataBlock)dataBlock; + } + } + + return null; + } + + + @Override + public DataUnitID getDUID() + { + return DataUnitID.ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL; + } + + /** + * List of identifiers provided by the message + */ + public abstract List getIdentifiers(); + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NAC:").append(getNAC()); + + if(!getPDUSequence().isComplete()) + { + sb.append(" *INCOMPLETE - RECEIVED ").append(getPDUSequence().getDataBlocks().size()).append("/") + .append(getPDUSequence().getHeader().getBlocksToFollowCount()).append(" DATA BLOCKS"); + } + + sb.append(" ").append(getPDUSequence().getHeader().toString()); + + sb.append(" DATA BLOCKS:").append(getPDUSequence().getDataBlocks().size()); + + if(!getPDUSequence().getDataBlocks().isEmpty()) + { + sb.append(" MSG:"); + + for(DataBlock dataBlock: getPDUSequence().getDataBlocks()) + { + sb.append(dataBlock.getMessage().toHexString()); + } + } + + sb.append(" COMPLETE:").append(getMessage().toHexString()); + + return sb.toString(); + } + + public PDUSequence getPDUSequence() + { + return mPDUSequence; + } + + private void extractMessage() + { + //There are 16 bits in the header + int length = 16; + + int blockCount = getPDUSequence().getHeader().getBlocksToFollowCount(); + + //Each block provides 108 bits/12 bytes and the final block uses 32-bits for CRC + length += (blockCount * 96); + + CorrectedBinaryMessage consolidatedMessage = new CorrectedBinaryMessage(length); + + //Transfer 2 octets from header + AMBTCHeader header = (AMBTCHeader)getPDUSequence().getHeader(); + + int dataOctetsValue = header.getDataOctets(); + consolidatedMessage.load(0, 16, dataOctetsValue); + + if(getPDUSequence().isComplete()) + { + int offset = 16; + for(DataBlock dataBlock: getPDUSequence().getDataBlocks()) + { + if(dataBlock instanceof UnconfirmedDataBlock) + { + BinaryMessage dataBlockMessage = dataBlock.getMessage(); + + for(int x = 0; x < dataBlockMessage.size(); x++) + { + if(dataBlockMessage.get(x)) + { + consolidatedMessage.set(x + offset); + } + } + + offset += dataBlockMessage.size(); + } + } + } + else + { + setValid(false); + } + + setMessage(consolidatedMessage); + } + + @Override + public int getBitsProcessedCount() + { + return getPDUSequence().getBitsProcessedCount(); + } + + @Override + public int getBitErrorsCount() + { + return getPDUSequence().getBitErrorsCount(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationQuery.java new file mode 100644 index 00000000..f8dbfd4c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationQuery.java @@ -0,0 +1,147 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCAuthenticationQuery extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_TARGET_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_RESERVED = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCAuthenticationQuery(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationResponse.java new file mode 100644 index 00000000..3dd38c77 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCAuthenticationResponse.java @@ -0,0 +1,161 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCAuthenticationResponse extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_AUTHENTICATION_VALUE = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] BLOCK_1_AUTHENTICATION_VALUE = {0, 1, 2, 3}; + + private String mAuthenticationValue; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mTargetAddress; + private Identifier mSourceId; + private List mIdentifiers; + + public AMBTCAuthenticationResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceId()); + if(getSourceId() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + if(getAuthenticationValue() != null) + { + sb.append(" AUTHENTICATION:").append(getAuthenticationValue()); + } + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public String getAuthenticationValue() + { + if(mAuthenticationValue == null && hasDataBlock(0) && hasDataBlock(1)) + { + mAuthenticationValue = getDataBlock(0).getMessage().getHex(BLOCK_0_AUTHENTICATION_VALUE, 14) + + getDataBlock(1).getMessage().getHex(BLOCK_1_AUTHENTICATION_VALUE, 2); + } + + return mAuthenticationValue; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCCallAlertRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCCallAlertRequest.java new file mode 100644 index 00000000..900e7cf8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCCallAlertRequest.java @@ -0,0 +1,142 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCCallAlertRequest extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_TARGET_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mTargetId; + private Identifier mSourceAddress; + private List mIdentifiers; + + public AMBTCCallAlertRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + return sb.toString(); + } + + public Identifier getTargetId() + { + if(mTargetId == null) + { + mTargetId = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null && hasDataBlock(0)) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCGroupAffiliationRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCGroupAffiliationRequest.java new file mode 100644 index 00000000..8ebe11b2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCGroupAffiliationRequest.java @@ -0,0 +1,144 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCGroupAffiliationRequest extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_GROUP_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] RESERVED = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mGroupId; + private Identifier mSourceAddress; + private List mIdentifiers; + + public AMBTCGroupAffiliationRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getSourceAddress() != null) + { + sb.append(" TO:").append(getGroupId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + return sb.toString(); + } + + public Identifier getGroupId() + { + if(mGroupId == null) + { + mGroupId = APCO25ToTalkgroup.createGroup(getHeader().getMessage().getInt(BLOCK_0_GROUP_ID)); + } + + return mGroupId; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null && hasDataBlock(0)) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getGroupId() != null) + { + mIdentifiers.add(getGroupId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCIndividualDataServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCIndividualDataServiceRequest.java new file mode 100644 index 00000000..6f1240fd --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCIndividualDataServiceRequest.java @@ -0,0 +1,154 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCIndividualDataServiceRequest extends AMBTCMessage +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_TARGET_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_RESERVED = {56, 57, 58, 59, 60, 61, 62, 63}; + + private DataServiceOptions mServiceOptions; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCIndividualDataServiceRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getDataServiceOptions()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public DataServiceOptions getDataServiceOptions() + { + if(mServiceOptions == null) + { + mServiceOptions = new DataServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCLocationRegistrationRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCLocationRegistrationRequest.java new file mode 100644 index 00000000..4c44aa3b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCLocationRegistrationRequest.java @@ -0,0 +1,185 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25IdentifierTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCLocationRegistrationRequest extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_PREVIOUS_LRA = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_GROUP_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mSourceAddress; + private Identifier mGroupAddress; + private Identifier mPreviousLra; + private List mIdentifiers; + + public AMBTCLocationRegistrationRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM ADDR:").append(getSourceAddress()); + if(getSourceId() != null) + { + sb.append(" FM ID:").append(getSourceId()); + } + if(getGroupAddress() != null) + { + sb.append(" TO:").append(getGroupAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + if(getPreviousLra() != null) + { + sb.append(" PREVIOUS LRA:").append(getPreviousLra()); + } + return sb.toString(); + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null && hasDataBlock(0)) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getPreviousLra() + { + if(mPreviousLra == null && hasDataBlock(0)) + { + mPreviousLra = APCO25Lra.create(getDataBlock(0).getMessage().getInt(BLOCK_0_PREVIOUS_LRA)); + } + + return mPreviousLra; + } + + public Identifier getSourceId() + { + if(mSourceId == null) + { + mSourceId = APCO25IdentifierTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null && hasDataBlock(0)) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getGroupAddress() != null) + { + mIdentifiers.add(getGroupAddress()); + } + if(getPreviousLra() != null) + { + mIdentifiers.add(getPreviousLra()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCMessageUpdateRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCMessageUpdateRequest.java new file mode 100644 index 00000000..d63b6ab8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCMessageUpdateRequest.java @@ -0,0 +1,152 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.message.APCO25ShortDataMessage; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCMessageUpdateRequest extends AMBTCMessage +{ + private static final int[] HEADER_MESSAGE = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_TARGET_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_RESERVED = {56, 57, 58, 59, 60, 61, 62, 63}; + + private Identifier mShortDataMessage; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCMessageUpdateRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SHORT DATA MSG:").append(getShortDataMessage()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getShortDataMessage() + { + if(mShortDataMessage == null) + { + mShortDataMessage = APCO25ShortDataMessage.create(getHeader().getMessage().getInt(HEADER_MESSAGE)); + } + + return mShortDataMessage; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCRoamingAddressRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCRoamingAddressRequest.java new file mode 100644 index 00000000..9603b2ea --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCRoamingAddressRequest.java @@ -0,0 +1,141 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCRoamingAddressRequest extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_TARGET_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mTargetId; + private Identifier mSourceAddress; + private List mIdentifiers; + + public AMBTCRoamingAddressRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + return sb.toString(); + } + + public Identifier getTargetId() + { + if(mTargetId == null) + { + mTargetId = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null && hasDataBlock(0)) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryRequest.java new file mode 100644 index 00000000..8d48a553 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryRequest.java @@ -0,0 +1,144 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCStatusQueryRequest extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_TARGET_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + private static final int[] RESERVED = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mTargetId; + private Identifier mSourceAddress; + private List mIdentifiers; + + public AMBTCStatusQueryRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getSourceAddress() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + return sb.toString(); + } + + public Identifier getTargetId() + { + if(mTargetId == null) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null && hasDataBlock(0)) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryResponse.java new file mode 100644 index 00000000..a5ed93f6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusQueryResponse.java @@ -0,0 +1,165 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCStatusQueryResponse extends AMBTCMessage +{ + private static final int[] HEADER_UNIT_STATUS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_USER_STATUS = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_TARGET_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_RESERVED = {56, 57, 58, 59, 60, 61, 62, 63}; + + private Identifier mUnitStatus; + private Identifier mUserStatus; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCStatusQueryResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" UNIT STATUS:").append(getUnitStatus()); + sb.append(" USER STATUS:").append(getUserStatus()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null) + { + mUnitStatus = APCO25Status.createUnitStatus(getHeader().getMessage().getInt(HEADER_UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null) + { + mUserStatus = APCO25Status.createUserStatus(getHeader().getMessage().getInt(HEADER_USER_STATUS)); + } + + return mUserStatus; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusUpdateRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusUpdateRequest.java new file mode 100644 index 00000000..d072b1f7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCStatusUpdateRequest.java @@ -0,0 +1,165 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCStatusUpdateRequest extends AMBTCMessage +{ + private static final int[] HEADER_UNIT_STATUS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_USER_STATUS = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_TARGET_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_RESERVED = {56, 57, 58, 59, 60, 61, 62, 63}; + + private Identifier mUnitStatus; + private Identifier mUserStatus; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCStatusUpdateRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" UNIT STATUS:").append(getUnitStatus()); + sb.append(" USER STATUS:").append(getUserStatus()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null) + { + mUnitStatus = APCO25Status.createUnitStatus(getHeader().getMessage().getInt(HEADER_UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null) + { + mUserStatus = APCO25Status.createUserStatus(getHeader().getMessage().getInt(HEADER_USER_STATUS)); + } + + return mUserStatus; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitAcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitAcknowledgeResponse.java new file mode 100644 index 00000000..4bf86a54 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitAcknowledgeResponse.java @@ -0,0 +1,159 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.Direction; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCUnitAcknowledgeResponse extends AMBTCMessage +{ + private static final int[] ACKNOWLEDGED_SERVICE_OPCODE = {66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_WACN = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + private static final int[] BLOCK_0_SYSTEM = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] BLOCK_0_TARGET_ID = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_RESERVED = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private Opcode mAcknowledgedService; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCUnitAcknowledgeResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" ACK SERVICE:").append(getAcknowledgedService()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Opcode getAcknowledgedService() + { + if(mAcknowledgedService == null) + { + mAcknowledgedService = Opcode.fromValue(getHeader().getMessage().getInt(ACKNOWLEDGED_SERVICE_OPCODE), + Direction.OUTBOUND, getHeader().getVendor()); + } + + return mAcknowledgedService; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 12; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceAnswerResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceAnswerResponse.java new file mode 100644 index 00000000..0dfd085f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceAnswerResponse.java @@ -0,0 +1,168 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.reference.AnswerResponse; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCUnitToUnitVoiceServiceAnswerResponse extends AMBTCMessage +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_ANSWER_RESPONSE = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_RESERVED = {0, 1, 2, 3, 4, 5, 6, 7}; + private static final int[] BLOCK_0_WACN = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27}; + private static final int[] BLOCK_0_SYSTEM = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_TARGET_ID = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63}; + + private VoiceServiceOptions mVoiceServiceOptions; + private AnswerResponse mAnswerResponse; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCUnitToUnitVoiceServiceAnswerResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + sb.append(" RESPONSE:").append(getAnswerResponse()); + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public AnswerResponse getAnswerResponse() + { + if(mAnswerResponse == null) + { + mAnswerResponse = AnswerResponse.fromValue(getHeader().getMessage().getInt(HEADER_ANSWER_RESPONSE)); + } + + return mAnswerResponse; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceRequest.java new file mode 100644 index 00000000..d70f6396 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/isp/AMBTCUnitToUnitVoiceServiceRequest.java @@ -0,0 +1,155 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCUnitToUnitVoiceServiceRequest extends AMBTCMessage +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_RESERVED = {0, 1, 2, 3, 4, 5, 6, 7}; + private static final int[] BLOCK_0_WACN = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27}; + private static final int[] BLOCK_0_SYSTEM = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_TARGET_ID = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mTargetId; + private List mIdentifiers; + + public AMBTCUnitToUnitVoiceServiceRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTargetId() != null) + { + sb.append(" TO:").append(getTargetId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null && hasDataBlock(0)) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetId() != null) + { + mIdentifiers.add(getTargetId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCAdjacentStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCAdjacentStatusBroadcast.java new file mode 100644 index 00000000..54bb9716 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCAdjacentStatusBroadcast.java @@ -0,0 +1,189 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCAdjacentStatusBroadcast extends AMBTCMessage implements IFrequencyBandReceiver +{ + + private static final int[] HEADER_LRA = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] HEADER_SYSTEM = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] HEADER_RFSS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_SITE = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {0, 1, 2, 3}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_UPLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] BLOCK_0_UPLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + private Identifier mSystem; + private Identifier mLocationRegistrationArea; + private Identifier mRfss; + private Identifier mSite; + private IChannelDescriptor mChannel; + private List mIdentifiers; + private List mChannels; + + public AMBTCAdjacentStatusBroadcast(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + + setValid(hasDataBlock(0)); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" RFSS:").append(getRfss()); + sb.append(" SITE:").append(getSite()); + sb.append(" CHAN:").append(getChannel()); + return sb.toString(); + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getHeader().getMessage().getInt(HEADER_SYSTEM)); + } + + return mSystem; + } + + public Identifier getLocationRegistrationArea() + { + if(mLocationRegistrationArea == null) + { + mLocationRegistrationArea = APCO25Lra.create(getHeader().getMessage().getInt(HEADER_LRA)); + } + + return mLocationRegistrationArea; + } + + public Identifier getRfss() + { + if(mRfss == null) + { + mRfss = APCO25Rfss.create(getHeader().getMessage().getInt(HEADER_RFSS)); + } + + return mRfss; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getHeader().getMessage().getInt(HEADER_SITE)); + } + + return mSite; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null && hasDataBlock(0)) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block = getDataBlock(0); + + if(isExtendedChannel()) + { + mChannel = APCO25ExplicitChannel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block.getMessage().getInt(BLOCK_0_UPLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + else + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getRfss() != null) + { + mIdentifiers.add(getRfss()); + } + if(getSite() != null) + { + mIdentifiers.add(getSite()); + } + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCCallAlert.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCCallAlert.java new file mode 100644 index 00000000..f623bc95 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCCallAlert.java @@ -0,0 +1,149 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call alert + */ +public class AMBTCCallAlert extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + + public AMBTCCallAlert(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + + return sb.toString(); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationQuery.java new file mode 100644 index 00000000..a44cf8e4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationQuery.java @@ -0,0 +1,149 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group affiliation query + */ +public class AMBTCGroupAffiliationQuery extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + + public AMBTCGroupAffiliationQuery(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + + return sb.toString(); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationResponse.java new file mode 100644 index 00000000..4c4e640e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupAffiliationResponse.java @@ -0,0 +1,241 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnnouncementTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group affiliation response + */ +public class AMBTCGroupAffiliationResponse extends AMBTCMessage +{ + private static final int[] HEADER_SOURCE_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_SOURCE_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SOURCE_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_GROUP_WACN = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59}; + private static final int[] BLOCK_0_GROUP_SYSTEM = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] BLOCK_0_GROUP_ID = {72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87}; + private static final int[] BLOCK_0_ANNOUNCEMENT_GROUP_ID = {88, 89, 90, 91, 92, 93, 94, 95}; + private static final int[] BLOCK_1_ANNOUNCEMENT_GROUP_ID = {0, 1, 2, 3, 4, 5, 6, 7}; + + private Identifier mTargetAddress; + private Identifier mSourceWacn; + private Identifier mSourceSystem; + private Identifier mSourceId; + private Identifier mGroupWacn; + private Identifier mGroupSystem; + private Identifier mGroupId; + private Identifier mAnnouncementGroupId; + private List mIdentifiers; + + public AMBTCGroupAffiliationResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getSourceWacn() != null) + { + sb.append(" FM WACN:").append(getSourceWacn()); + } + if(getSourceSystem() != null) + { + sb.append(" FM SYSTEM:").append(getSourceSystem()); + } + if(getSourceId() != null) + { + sb.append(" FM ID:").append(getSourceId()); + } + if(getGroupWacn() != null) + { + sb.append(" GRP WACN:").append(getGroupWacn()); + } + if(getGroupSystem() != null) + { + sb.append(" GRP SYSTEM:").append(getGroupSystem()); + } + if(getGroupId() != null) + { + sb.append(" GRP ID:").append(getGroupId()); + } + if(getAnnouncementGroupId() != null) + { + sb.append(" ANNOUNCEMENT GRP:").append(getAnnouncementGroupId()); + } + + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceWacn() + { + if(mSourceWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_SOURCE_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_WACN); + mSourceWacn = APCO25Wacn.create(value); + } + + return mSourceWacn; + } + + public Identifier getSourceSystem() + { + if(mSourceSystem == null && hasDataBlock(0)) + { + mSourceSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_SYSTEM)); + } + + return mSourceSystem; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getGroupWacn() + { + if(mGroupWacn == null && hasDataBlock(0)) + { + mGroupWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_GROUP_WACN)); + } + + return mGroupWacn; + } + + public Identifier getGroupSystem() + { + if(mGroupSystem == null && hasDataBlock(0)) + { + mGroupSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_GROUP_SYSTEM)); + } + + return mGroupSystem; + } + + public Identifier getGroupId() + { + if(mGroupId == null && hasDataBlock(0)) + { + mGroupId = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_GROUP_ID)); + } + + return mGroupId; + } + + public Identifier getAnnouncementGroupId() + { + if(mAnnouncementGroupId == null && hasDataBlock(0) && hasDataBlock(1)) + { + int id = getDataBlock(0).getMessage().getInt(BLOCK_0_ANNOUNCEMENT_GROUP_ID); + id <<= 8; + id += getDataBlock(1).getMessage().getInt(BLOCK_1_ANNOUNCEMENT_GROUP_ID); + + mAnnouncementGroupId = APCO25AnnouncementTalkgroup.create(id); + } + + return mAnnouncementGroupId; + } + + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getSourceWacn() != null) + { + mIdentifiers.add(getSourceWacn()); + } + if(getSourceSystem() != null) + { + mIdentifiers.add(getSourceSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getGroupWacn() != null) + { + mIdentifiers.add(getGroupWacn()); + } + if(getGroupSystem() != null) + { + mIdentifiers.add(getGroupSystem()); + } + if(getGroupId() != null) + { + mIdentifiers.add(getGroupId()); + } + if(getAnnouncementGroupId() != null) + { + mIdentifiers.add(getAnnouncementGroupId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupDataChannelGrant.java new file mode 100644 index 00000000..2e0a9490 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupDataChannelGrant.java @@ -0,0 +1,166 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCGroupDataChannelGrant extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_RESERVED = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_UPLINK_FREQUENCY_BAND = {32, 33, 34, 35}; + private static final int[] BLOCK_0_UPLINK_CHANNEL_NUMBER = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_GROUP_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private DataServiceOptions mDataServiceOptions; + private APCO25Channel mChannel; + private Identifier mSourceAddress; + private Identifier mGroupAddress; + private List mIdentifiers; + private List mChannels; + + public AMBTCGroupDataChannelGrant(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" CHAN:").append(getChannel()); + sb.append(" SERVICE OPTIONS:").append(getDataServiceOptions()); + return sb.toString(); + } + + public DataServiceOptions getDataServiceOptions() + { + if(mDataServiceOptions == null) + { + mDataServiceOptions = new DataServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mDataServiceOptions; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null && hasDataBlock(0)) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null && hasDataBlock(0)) + { + UnconfirmedDataBlock block = getDataBlock(0); + + if(isExtendedChannel()) + { + mChannel = APCO25ExplicitChannel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block.getMessage().getInt(BLOCK_0_UPLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + return mChannel; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getGroupAddress() != null) + { + mIdentifiers.add(getGroupAddress()); + } + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupVoiceChannelGrant.java new file mode 100644 index 00000000..fe116501 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCGroupVoiceChannelGrant.java @@ -0,0 +1,174 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCGroupVoiceChannelGrant extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_RESERVED = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_UPLINK_FREQUENCY_BAND = {32, 33, 34, 35}; + private static final int[] BLOCK_0_UPLINK_CHANNEL_NUMBER = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_GROUP_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mSourceAddress; + private Identifier mGroupAddress; + private List mIdentifiers; + private List mChannels; + + public AMBTCGroupVoiceChannelGrant(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" CHAN:").append(getChannel()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null && hasDataBlock(0)) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block = getDataBlock(0); + + if(isExtendedChannel()) + { + mChannel = APCO25ExplicitChannel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block.getMessage().getInt(BLOCK_0_UPLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + if(mChannel == null) + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getGroupAddress() != null) + { + mIdentifiers.add(getGroupAddress()); + } + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCIndividualDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCIndividualDataChannelGrant.java new file mode 100644 index 00000000..07583c65 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCIndividualDataChannelGrant.java @@ -0,0 +1,236 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCIndividualDataChannelGrant extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_SOURCE_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {80, 81, 82, 83}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + private static final int[] BLOCK_1_UPLINK_FREQUENCY_BAND = {0, 1, 2, 3}; + private static final int[] BLOCK_1_UPLINK_CHANNEL_NUMBER = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + private DataServiceOptions mDataServiceOptions; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + private APCO25Channel mChannel; + private List mChannels; + + public AMBTCIndividualDataChannelGrant(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + sb.append(" TO:").append(getTargetAddress()); + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getDataServiceOptions()); + return sb.toString(); + } + + public DataServiceOptions getDataServiceOptions() + { + if(mDataServiceOptions == null) + { + mDataServiceOptions = new DataServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mDataServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + } + + return mIdentifiers; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_1_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block0 = getDataBlock(0); + + if(isExtendedChannel() && hasDataBlock(1)) + { + UnconfirmedDataBlock block1 = getDataBlock(1); + mChannel = APCO25ExplicitChannel.create(block0.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block0.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block1.getMessage().getInt(BLOCK_1_UPLINK_FREQUENCY_BAND), + block1.getMessage().getInt(BLOCK_1_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block0.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block0.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + if(mChannel == null) + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCMessageUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCMessageUpdate.java new file mode 100644 index 00000000..ccf2e62d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCMessageUpdate.java @@ -0,0 +1,170 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.message.APCO25ShortDataMessage; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Message update + */ +public class AMBTCMessageUpdate extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_SDM = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mTargetAddress; + private Identifier mShortDataMessage; + private List mIdentifiers; + + public AMBTCMessageUpdate(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + if(getShortDataMessage() != null) + { + sb.append(" SHORT DATA MESSAGE:").append(getShortDataMessage()); + } + + return sb.toString(); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getShortDataMessage() + { + if(mShortDataMessage == null && hasDataBlock(0)) + { + mShortDataMessage = APCO25ShortDataMessage.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SDM)); + } + + return mShortDataMessage; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getShortDataMessage() != null) + { + mIdentifiers.add(getShortDataMessage()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCProtectionParameterBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCProtectionParameterBroadcast.java new file mode 100644 index 00000000..bd51e2e9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCProtectionParameterBroadcast.java @@ -0,0 +1,146 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Protection (encryption) parameter broadcast + */ +public class AMBTCProtectionParameterBroadcast extends AMBTCMessage +{ + private static final int[] HEADER_ALGORITHM_ID = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_KEY_ID = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_INBOUND_MESSAGE_INDICATOR_1 = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_INBOUND_MESSAGE_INDICATOR_2 = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87}; + private static final int[] BLOCK_0_OUTBOUND_MESSAGE_INDICATOR_1 = {88, 89, 90, 91, 92, 93, 94, 95}; + private static final int[] BLOCK_1_OUTBOUND_MESSAGE_INDICATOR_2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_1_OUTBOUND_MESSAGE_INDICATOR_3 = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private Identifier mTargetAddress; + private Identifier mEncryptionKey; + private String mInboundMessageIndicator; + private String mOutboundMessageIndicator; + private List mIdentifiers; + + public AMBTCProtectionParameterBroadcast(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getEncryptionKey() != null) + { + sb.append(" ENCRYPTION KEY:").append(getEncryptionKey()); + } + if(getInboundMessageIndicator() != null) + { + sb.append(" INBOUND MI:").append(getInboundMessageIndicator()); + } + if(getOutboundMessageIndicator() != null) + { + sb.append(" OUTBOUND MI:").append(getOutboundMessageIndicator()); + } + + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getEncryptionKey() + { + if(mEncryptionKey == null && hasDataBlock(0)) + { + mEncryptionKey = APCO25EncryptionKey.create(getHeader().getMessage().getInt(HEADER_ALGORITHM_ID), + getDataBlock(0).getMessage().getInt(BLOCK_0_KEY_ID)); + } + + return mEncryptionKey; + } + + public String getInboundMessageIndicator() + { + if(mInboundMessageIndicator == null && hasDataBlock(0)) + { + mInboundMessageIndicator = getDataBlock(0).getMessage().getHex(BLOCK_0_INBOUND_MESSAGE_INDICATOR_1, 10) + + getDataBlock(0).getMessage().getHex(BLOCK_0_INBOUND_MESSAGE_INDICATOR_2, 8); + } + + return mInboundMessageIndicator; + } + + public String getOutboundMessageIndicator() + { + if(mOutboundMessageIndicator == null && hasDataBlock(0) && hasDataBlock(1)) + { + mOutboundMessageIndicator = getDataBlock(0).getMessage().getHex(BLOCK_0_OUTBOUND_MESSAGE_INDICATOR_1, 2) + + getDataBlock(1).getMessage().getHex(BLOCK_1_OUTBOUND_MESSAGE_INDICATOR_2, 8) + + getDataBlock(1).getMessage().getHex(BLOCK_1_OUTBOUND_MESSAGE_INDICATOR_3, 8); + } + + return mOutboundMessageIndicator; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getEncryptionKey() != null) + { + mIdentifiers.add(getEncryptionKey()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressResponse.java new file mode 100644 index 00000000..4afb047f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressResponse.java @@ -0,0 +1,169 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Roaming address update message. Assigns WACN and SYSTEM combinations to a target address. + * + * Note: this is a variable block length message containing between 2 and 8 possible combination + * values. This class only parses out the first WACN an SYSTEM combination. Further parsing + * support is required to fully extract the additional combinations, but they are basically stacked + * one after another in each of the subsequent data blocks with the final data block containing the + * 4 CRC octets. + */ +public class AMBTCRoamingAddressResponse extends AMBTCMessage +{ + private static final int LAST_MESSAGE_FLAG = 64; + private static final int[] HEADER_MSN = {68, 69, 70, 71}; + private static final int[] HEADER_WACN_A = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN_A = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + private static final int[] BLOCK_0_SYSTEM_A = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mTargetAddress; + private List mIdentifiers; + + public AMBTCRoamingAddressResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN A:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM A:").append(getSystem()); + } + sb.append(" MSN:").append(getMessageSequenceNumber()); + if(isLastMessage()) + { + sb.append(" (FINAL)"); + } + + sb.append(" TOTAL WACN/SYSTEM COMBOS IN MESSAGE:").append(getWacnCount()); + + return sb.toString(); + } + + private int getWacnCount() + { + int dataBlockCount = getPDUSequence().getDataBlocks().size(); + + switch(dataBlockCount) + { + case 1: + return 2; + case 2: + return 5; + case 3: + return 8; + default: + return 2; + } + } + + public boolean isLastMessage() + { + return getHeader().getMessage().get(LAST_MESSAGE_FLAG); + } + + private int getMessageSequenceNumber() + { + return getHeader().getMessage().getInt(HEADER_MSN); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN_A); + value <<= 12; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN_A); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM_A)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressUpdate.java new file mode 100644 index 00000000..a3faaea9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCRoamingAddressUpdate.java @@ -0,0 +1,169 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Roaming address update message. Assigns WACN and SYSTEM combinations to a target address. + * + * Note: this is a variable block length message containing between 2 and 8 possible combination + * values. This class only parses out the first WACN an SYSTEM combination. Further parsing + * support is required to fully extract the additional combinations, but they are basically stacked + * one after another in each of the subsequent data blocks with the final data block containing the + * 4 CRC octets. + */ +public class AMBTCRoamingAddressUpdate extends AMBTCMessage +{ + private static final int LAST_MESSAGE_FLAG = 64; + private static final int[] HEADER_MSN = {68, 69, 70, 71}; + private static final int[] HEADER_WACN_A = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN_A = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + private static final int[] BLOCK_0_SYSTEM_A = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mTargetAddress; + private List mIdentifiers; + + public AMBTCRoamingAddressUpdate(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN A:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM A:").append(getSystem()); + } + sb.append(" MSN:").append(getMessageSequenceNumber()); + if(isLastMessage()) + { + sb.append(" (FINAL)"); + } + + sb.append(" TOTAL WACN/SYSTEM COMBOS IN MESSAGE:").append(getWacnCount()); + + return sb.toString(); + } + + private int getWacnCount() + { + int dataBlockCount = getPDUSequence().getDataBlocks().size(); + + switch(dataBlockCount) + { + case 1: + return 2; + case 2: + return 5; + case 3: + return 8; + default: + return 2; + } + } + + public boolean isLastMessage() + { + return getHeader().getMessage().get(LAST_MESSAGE_FLAG); + } + + private int getMessageSequenceNumber() + { + return getHeader().getMessage().getInt(HEADER_MSN); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN_A); + value <<= 12; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN_A); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM_A)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusQuery.java new file mode 100644 index 00000000..fe300c24 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusQuery.java @@ -0,0 +1,149 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status Query + */ +public class AMBTCStatusQuery extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + + public AMBTCStatusQuery(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + + return sb.toString(); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusUpdate.java new file mode 100644 index 00000000..23b6e522 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCStatusUpdate.java @@ -0,0 +1,182 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status Query + */ +public class AMBTCStatusUpdate extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_UNIT_STATUS = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_USER_STATUS = {48, 49, 50, 51, 52, 53, 54, 55}; + + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mTargetAddress; + private Identifier mUnitStatus; + private Identifier mUserStatus; + private List mIdentifiers; + + public AMBTCStatusUpdate(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + if(getUnitStatus() != null) + { + sb.append(" UNIT STATUS:").append(getUnitStatus()); + } + if(getUserStatus() != null) + { + sb.append(" USER STATUS:").append(getUserStatus()); + } + + return sb.toString(); + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null && hasDataBlock(0)) + { + mUnitStatus = APCO25Status.createUnitStatus(getDataBlock(0).getMessage().getInt(BLOCK_0_UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null && hasDataBlock(0)) + { + mUserStatus = APCO25Status.createUserStatus(getDataBlock(0).getMessage().getInt(BLOCK_0_USER_STATUS)); + } + + return mUserStatus; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrant.java new file mode 100644 index 00000000..46467cd8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrant.java @@ -0,0 +1,171 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCTelephoneInterconnectChannelGrant extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {0, 1, 2, 3}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_UPLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] BLOCK_0_UPLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_CALL_TIMER = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_RESERVED = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mAnyAddress; + private List mIdentifiers; + private List mChannels; + + public AMBTCTelephoneInterconnectChannelGrant(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ADDR:").append(getAnyAddress()); + if(getChannel() != null) + { + sb.append(" CHAN:").append(getChannel()); + } + sb.append(" CALL TIMER:").append(getCallTimer()).append("ms"); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public long getCallTimer() + { + if(hasDataBlock(0)) + { + return getDataBlock(0).getMessage().getInt(BLOCK_0_CALL_TIMER) * 100; //milliseconds + } + + return 0; + } + + public Identifier getAnyAddress() + { + if(mAnyAddress == null) + { + mAnyAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mAnyAddress; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block = getDataBlock(0); + + if(isExtendedChannel()) + { + mChannel = APCO25ExplicitChannel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block.getMessage().getInt(BLOCK_0_UPLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + if(mChannel == null) + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getAnyAddress() != null) + { + mIdentifiers.add(getAnyAddress()); + } + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrantUpdate.java new file mode 100644 index 00000000..a54b7623 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCTelephoneInterconnectChannelGrantUpdate.java @@ -0,0 +1,171 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCTelephoneInterconnectChannelGrantUpdate extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {0, 1, 2, 3}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_UPLINK_FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] BLOCK_0_UPLINK_CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_CALL_TIMER = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_RESERVED = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mAnyAddress; + private List mIdentifiers; + private List mChannels; + + public AMBTCTelephoneInterconnectChannelGrantUpdate(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ADDR:").append(getAnyAddress()); + if(getChannel() != null) + { + sb.append(" CHAN:").append(getChannel()); + } + sb.append(" CALL TIMER:").append(getCallTimer()).append("ms"); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public long getCallTimer() + { + if(hasDataBlock(0)) + { + return getDataBlock(0).getMessage().getInt(BLOCK_0_CALL_TIMER) * 100; //milliseconds + } + + return 0; + } + + public Identifier getAnyAddress() + { + if(mAnyAddress == null) + { + mAnyAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mAnyAddress; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block = getDataBlock(0); + + if(isExtendedChannel()) + { + mChannel = APCO25ExplicitChannel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block.getMessage().getInt(BLOCK_0_UPLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + if(mChannel == null) + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getAnyAddress() != null) + { + mIdentifiers.add(getAnyAddress()); + } + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitRegistrationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitRegistrationResponse.java new file mode 100644 index 00000000..dfccbb5a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitRegistrationResponse.java @@ -0,0 +1,183 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.reference.Response; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit registration response - when the source unit does not match the WACN id for the current site + */ +public class AMBTCUnitRegistrationResponse extends AMBTCMessage +{ + private static final int[] HEADER_WACN = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3}; + private static final int[] BLOCK_0_SYSTEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SOURCE_ID = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_SOURCE_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] BLOCK_0_RESPONSE = {70, 71}; + + private Response mResponse; + private Identifier mTargetAddress; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mSourceAddress; + private List mIdentifiers; + + public AMBTCUnitRegistrationResponse(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getTargetAddress() != null) + { + sb.append(" TO:").append(getTargetAddress()); + } + if(getSourceId() != null) + { + sb.append(" FM ID:").append(getSourceId()); + } + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + if(getSourceAddress() != null) + { + sb.append(" FM ADDRESS:").append(getSourceAddress()); + } + + return sb.toString(); + } + + public Response getResponse() + { + if(mResponse == null && hasDataBlock(0)) + { + mResponse = Response.fromValue(getDataBlock(0).getMessage().getInt(BLOCK_0_RESPONSE)); + } + + return mResponse; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + int value = getHeader().getMessage().getInt(HEADER_WACN); + value <<= 4; + value += getDataBlock(0).getMessage().getInt(BLOCK_0_WACN); + mWacn = APCO25Wacn.create(value); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null && hasDataBlock(0)) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getDataBlock(0).getMessage().getInt(BLOCK_0_SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitAnswerRequest.java new file mode 100644 index 00000000..432c4cce --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitAnswerRequest.java @@ -0,0 +1,154 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCUnitToUnitAnswerRequest extends AMBTCMessage +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_RESERVED = {0, 1, 2, 3, 4, 5, 6, 7}; + private static final int[] BLOCK_0_WACN = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; + private static final int[] BLOCK_0_SYSTEM = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] BLOCK_0_SOURCE_ID = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + + public AMBTCUnitToUnitAnswerRequest(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + sb.append(" TO:").append(getTargetAddress()); + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceId() + { + if(mSourceId == null) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(HEADER_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrant.java new file mode 100644 index 00000000..b76327b9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrant.java @@ -0,0 +1,236 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCUnitToUnitVoiceServiceChannelGrant extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_SOURCE_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {80, 81, 82, 83}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + private static final int[] BLOCK_1_UPLINK_FREQUENCY_BAND = {0, 1, 2, 3}; + private static final int[] BLOCK_1_UPLINK_CHANNEL_NUMBER = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + private APCO25Channel mChannel; + private List mChannels; + + public AMBTCUnitToUnitVoiceServiceChannelGrant(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + sb.append(" TO:").append(getTargetAddress()); + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + } + + return mIdentifiers; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_1_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block0 = getDataBlock(0); + + if(isExtendedChannel() && hasDataBlock(1)) + { + UnconfirmedDataBlock block1 = getDataBlock(1); + mChannel = APCO25ExplicitChannel.create(block0.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block0.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block1.getMessage().getInt(BLOCK_1_UPLINK_FREQUENCY_BAND), + block1.getMessage().getInt(BLOCK_1_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block0.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block0.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + if(mChannel == null) + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrantUpdate.java new file mode 100644 index 00000000..72b197bc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/ambtc/osp/AMBTCUnitToUnitVoiceServiceChannelGrantUpdate.java @@ -0,0 +1,237 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class AMBTCUnitToUnitVoiceServiceChannelGrantUpdate extends AMBTCMessage implements IFrequencyBandReceiver +{ + private static final int[] HEADER_SERVICE_OPTIONS = {64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] HEADER_RESERVED = {72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_WACN = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + private static final int[] BLOCK_0_SYSTEM = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] BLOCK_0_SOURCE_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] BLOCK_0_TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] BLOCK_0_DOWNLINK_FREQUENCY_BAND = {80, 81, 82, 83}; + private static final int[] BLOCK_0_DOWNLINK_CHANNEL_NUMBER = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + private static final int[] BLOCK_1_UPLINK_FREQUENCY_BAND = {0, 1, 2, 3}; + private static final int[] BLOCK_1_UPLINK_CHANNEL_NUMBER = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mWacn; + private Identifier mSystem; + private Identifier mSourceAddress; + private Identifier mSourceId; + private Identifier mTargetAddress; + private List mIdentifiers; + private APCO25Channel mChannel; + private List mChannels; + + public AMBTCUnitToUnitVoiceServiceChannelGrantUpdate(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(getSourceId() != null) + { + sb.append(" FM:").append(getSourceId()); + } + sb.append(" TO:").append(getTargetAddress()); + if(getWacn() != null) + { + sb.append(" WACN:").append(getWacn()); + } + if(getSystem() != null) + { + sb.append(" SYSTEM:").append(getSystem()); + } + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getHeader().getMessage().getInt(HEADER_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getWacn() + { + if(mWacn == null && hasDataBlock(0)) + { + mWacn = APCO25Wacn.create(getDataBlock(0).getMessage().getInt(BLOCK_0_WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null && hasDataBlock(0)) + { + mSystem = APCO25System.create(getDataBlock(0).getMessage().getInt(BLOCK_0_SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null && hasDataBlock(0)) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(BLOCK_0_SOURCE_ID)); + } + + return mSourceId; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null && hasDataBlock(0)) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getDataBlock(0).getMessage().getInt(BLOCK_0_TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + if(getSourceAddress() != null) + { + mIdentifiers.add(getSourceAddress()); + } + if(getTargetAddress() != null) + { + mIdentifiers.add(getTargetAddress()); + } + if(getSourceId() != null) + { + mIdentifiers.add(getSourceId()); + } + if(getWacn() != null) + { + mIdentifiers.add(getWacn()); + } + if(getSystem() != null) + { + mIdentifiers.add(getSystem()); + } + } + + return mIdentifiers; + } + + public boolean isExtendedChannel() + { + return hasDataBlock(0) && + (getDataBlock(0).getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER) != + getDataBlock(0).getMessage().getInt(BLOCK_1_UPLINK_CHANNEL_NUMBER)); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(hasDataBlock(0)) + { + UnconfirmedDataBlock block0 = getDataBlock(0); + + if(isExtendedChannel() && hasDataBlock(1)) + { + UnconfirmedDataBlock block1 = getDataBlock(1); + + mChannel = APCO25ExplicitChannel.create(block0.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block0.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER), + block1.getMessage().getInt(BLOCK_1_UPLINK_FREQUENCY_BAND), + block1.getMessage().getInt(BLOCK_1_UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(block0.getMessage().getInt(BLOCK_0_DOWNLINK_FREQUENCY_BAND), + block0.getMessage().getInt(BLOCK_0_DOWNLINK_CHANNEL_NUMBER)); + } + } + + if(mChannel == null) + { + mChannel = APCO25Channel.create(-1, 0); + } + } + + return mChannel; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + + if(getChannel() != null) + { + mChannels.add(getChannel()); + } + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/ConfirmedDataBlock.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/ConfirmedDataBlock.java new file mode 100644 index 00000000..2b7cd345 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/ConfirmedDataBlock.java @@ -0,0 +1,89 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.block; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.edac.CRCP25; +import io.github.dsheirer.edac.trellis.ViterbiDecoder_3_4_P25; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConfirmedDataBlock extends DataBlock +{ + private final static Logger mLog = LoggerFactory.getLogger(ConfirmedDataBlock.class); + + public static final int[] SEQUENCE_NUMBER = {0, 1, 2, 3, 4, 5, 6}; + public static final int PAYLOAD_START = 16; + public static final int PAYLOAD_END = 144; + + private static final ViterbiDecoder_3_4_P25 VITERBI_THREE_QUARTER_RATE_DECODER = new ViterbiDecoder_3_4_P25(); + private CorrectedBinaryMessage mDecodedMessage; + private boolean mValid; + + public ConfirmedDataBlock(CorrectedBinaryMessage correctedBinaryMessage) + { + mDecodedMessage = VITERBI_THREE_QUARTER_RATE_DECODER.decode(correctedBinaryMessage); + mDecodedMessage.incrementCorrectedBitCount(correctedBinaryMessage.getCorrectedBitCount()); + + checkCRC(); + } + + /** + * Sequence number for the data block. + * + * @return sequence number between 0 - 127, or -1 if there was an error during decoding. + */ + public int getSequenceNumber() + { + if(mDecodedMessage != null) + { + return mDecodedMessage.getInt(SEQUENCE_NUMBER); + } + + return -1; + } + + private void checkCRC() + { + CRC crc = CRCP25.checkCRC9(mDecodedMessage, 0); + mValid = (crc == CRC.PASSED || crc == CRC.CORRECTED); + } + + @Override + public BinaryMessage getMessage() + { + return mDecodedMessage.getSubMessage(PAYLOAD_START, PAYLOAD_END); + } + + @Override + public int getBitErrorsCount() + { + return mDecodedMessage.getCorrectedBitCount(); + } + + @Override + public boolean isValid() + { + return mValid; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/DataBlock.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/DataBlock.java new file mode 100644 index 00000000..0e72a806 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/DataBlock.java @@ -0,0 +1,55 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.pdu.block; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.message.IBitErrorProvider; + +/** + * Packet Data Unit data block. + */ +public abstract class DataBlock implements IBitErrorProvider +{ + /** + * Decoded binary message payload. + */ + public abstract BinaryMessage getMessage(); + + /** + * Number of bits that were processed for this data block (196). + */ + @Override + public int getBitsProcessedCount() + { + //Interleaved message length of a PDU packet is 196 bits + 6 status bits + return 196; + } + + /** + * Number of bit errors detected during the decoding process + */ + @Override + public abstract int getBitErrorsCount(); + + /** + * Indicates if the data block passes any block level error correction + */ + public abstract boolean isValid(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/UnconfirmedDataBlock.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/UnconfirmedDataBlock.java new file mode 100644 index 00000000..91421131 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/block/UnconfirmedDataBlock.java @@ -0,0 +1,71 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.block; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.trellis.ViterbiDecoder_1_2_P25; + +/** + * P25 Unconfirmed Data block that uses 1/2 rate trellis coding. + */ +public class UnconfirmedDataBlock extends DataBlock +{ + private static final ViterbiDecoder_1_2_P25 VITERBI_HALF_RATE_DECODER = new ViterbiDecoder_1_2_P25(); + private CorrectedBinaryMessage mDecodedMessage; + + /** + * Constructs an unconfirmed data block from the deinterleaved message. + * @param correctedBinaryMessage containing deinterleaved 196-bit data block. + */ + public UnconfirmedDataBlock(CorrectedBinaryMessage correctedBinaryMessage) + { + mDecodedMessage = VITERBI_HALF_RATE_DECODER.decode(correctedBinaryMessage); + mDecodedMessage.incrementCorrectedBitCount(correctedBinaryMessage.getCorrectedBitCount()); + } + + /** + * Message payload + */ + @Override + public CorrectedBinaryMessage getMessage() + { + return mDecodedMessage; + } + + /** + * Number of bit errors detected/corrected during viterbi decoding. + */ + @Override + public int getBitErrorsCount() + { + return mDecodedMessage.getCorrectedBitCount(); + } + + /** + * Indicates if this data block passes any block-level CRC. + * @return true always for unconfirmed data blocks. + */ + @Override + public boolean isValid() + { + return true; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java deleted file mode 100644 index 41f362ea..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java +++ /dev/null @@ -1,207 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.PDUType; -import io.github.dsheirer.module.decode.p25.reference.Vendor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PDUConfirmedMessage extends PDUMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( PDUConfirmedMessage.class ); - - public static final int SEQUENCE_RESET_FLAG = 128; //SYN - public static final int[] PACKET_SEQUENCE_NUMBER = { 129,130,131 }; - public static final int FINAL_FRAGMENT_FLAG = 132; - public static final int[] FRAGMENT_SEQUENCE_NUMBER = { 133,134,135 }; - public static final int[] PDU_TYPE = { 176,177,178,179 }; - - public PDUConfirmedMessage( BinaryMessage message, AliasList aliasList ) - { - super( message, DataUnitID.PDUC, aliasList ); - - checkCRC(); - } - - /** - * Constructor to support subclassing of this message. Initial construction - * of the message enables CRC checks to be calculated and message type to - * be inspected so that we can construct an accurate subclass message, - * without having to recalculate CRC checksums. - */ - protected PDUConfirmedMessage( PDUConfirmedMessage message ) - { - super( message.getSourceMessage(), DataUnitID.PDUC, message.getAliasList() ); - mCRC = message.getCRCResults(); - } - - private void checkCRC() - { - int blocks = getBlocksToFollowCount(); - - /* The NID and Header have already passed CRC */ - mCRC = new CRC[ 2 + blocks ]; - mCRC[ 0 ] = CRC.PASSED; - mCRC[ 1 ] = CRC.PASSED; - - for( int x = 0; x < getBlocksToFollowCount(); x++ ) - { - /* Data blocks start at 160 and every 144 thereafter */ - mCRC[ x + 2 ] = CRCP25.checkCRC9( mMessage, 160 + ( x * 144 ) ); - } - - //TODO: check the final 4-byte end of block sequence CRC - } - - /* Override for now */ - public boolean isValid() - { - return true; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "[" + this.getClass() + "]" ); - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - sb.append( " " ); - sb.append( getDirection() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "] LLID:" ); - sb.append( getLogicalLinkID() ); - - sb.append( " " ); - sb.append( getFormat().getLabel() ); - sb.append( " SAP:" ); - sb.append( getServiceAccessPoint().getLabel() ); - - if( getVendor() != Vendor.STANDARD ) - { - sb.append( " VEND:" ); - sb.append( getVendor().getLabel() ); - } - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " BLOCKS:" ); - sb.append( getBlocksToFollowCount() ); - - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - /** - * Packet Data Unit Type - */ - public PDUType getPDUType() - { - return PDUType.fromValue( mMessage.getInt( PDU_TYPE ), isOutbound() ); - } - - - - /** - * Number of bytes/octets contained in the message, exclusive of the data - * header. - */ - public int getOctetCount() - { - /* 16 octets per block, and 12 octets in final block */ - int count = getBlocksToFollowCount() * 16 - 4; - - int padding = getPadOctetCount(); - - /* Hack: motorola sndcp packet data control - when it says there are 15 - * pad octets and only 1 data block of 12 data octets, that means there - * are more pad octets than available data and they appear to suppress - * the final padded data block containing the 12 pad octets. The data - * block count should be 2, but it says 1, so they're either not sending - * the final block, or there is an error in the block count */ - if( padding > count ) - { - count -= ( padding - 12 ); - } - else - { - count -= padding; - } - - - count -= getDataHeaderOffset(); - - return count; - } - - /** - * Indicates that the receiver should accept this packet, despite the - * apparent out-of-order packet and fragment sequence numbering. This - * indicates that something happened to the synchronization between - * the tower and the radio and now the tower commands the radio to reset - * the sequence numbers and accept this packet/fragment instead of discarding - * it as a duplicate. - */ - public boolean isSequenceReset() - { - return mMessage.get( SEQUENCE_RESET_FLAG ); - } - - /** - * Packet sequence number. Numbers run from 0 to 7 and will continually - * roll-over if there are more than 8 total packets. - */ - public int getPacketSequenceNumber() - { - return mMessage.getInt( PACKET_SEQUENCE_NUMBER ); - } - - /** - * Indicates that this packet fragment is the final fragment - */ - public boolean isFinalFragment() - { - return mMessage.get( FINAL_FRAGMENT_FLAG ); - } - - /** - * Packet fragment sequence number - */ - public int getFragmentSequenceNumber() - { - return mMessage.getInt( FRAGMENT_SEQUENCE_NUMBER ); - } - - public String toString() - { - return getMessage(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java deleted file mode 100644 index 0459cfa3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PDUConfirmedMessageFactory -{ - public final static Logger mLog = - LoggerFactory.getLogger( PDUConfirmedMessage.class ); - - public static PDUConfirmedMessage getMessage( PDUConfirmedMessage message ) - { - switch( message.getServiceAccessPoint() ) - { - case ADDRESS_RESOLUTION_PROTOCOL: - break; - case CHANNEL_REASSIGNMENT: - break; - case CIRCUIT_DATA: - break; - case CIRCUIT_DATA_CONTROL: - break; - case ENCRYPTED_KEY_MANAGEMENT_MESSAGE: - break; - case ENCRYPTED_TRUNKING_CONTROL: - break; - case ENCRYPTED_USER_DATA: - break; - case EXTENDED_ADDRESS: - break; - case MR_CONFIGURATION: - break; - case MR_LOOPBACK: - break; - case MR_OUT_OF_SERVICE: - break; - case MR_PAGING: - break; - case MR_STATISTICS: - break; - case PACKET_DATA: - break; - case REGISTRATION_AND_AUTHORIZATION: - break; - /* Deliberate fall-through */ - case SNDCP_PACKET_DATA_CONTROL: - case UNENCRYPTED_USER_DATA: - switch( message.getPDUType() ) - { - case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: - return new SNDCPActivateTDSContextAccept( message ); - case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: - return new SNDCPActivateTDSContextReject( message ); - case SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: - return new SNDCPActivateTDSContextRequest( message ); - case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: - case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: - return new SNDCPDeactivateTDSContext( message ); - case SNDCP_RF_CONFIRMED_DATA: - return new SNDCPUserData( message ); - case SNDCP_RF_UNCONFIRMED_DATA: - break; - default: - break; - } - break; - case SYSTEM_CONFIGURATION: - break; - case UNENCRYPTED_KEY_MANAGEMENT_MESSAGE: - break; - case UNENCRYPTED_TRUNKING_CONTROL: - break; - default: - - } - - return message; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java deleted file mode 100644 index 26b04ce2..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PDUTypeUnknown extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( PDUTypeUnknown.class ); - - public PDUTypeUnknown( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " PACKET DATA UNIT CONFIRMED - " ); - sb.append( getPDUType().getLabel() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - sb.append( mMessage.toString() ); - - return sb.toString(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PacketData.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PacketData.java deleted file mode 100644 index c07f556b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/PacketData.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PacketData extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( PacketData.class ); - - public static final int DATA_BLOCK_START = 176; - - public PacketData( BinaryMessage message, AliasList aliasList ) - { - super( message, aliasList ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - - - sb.append( " PACKET DATA" ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - if( !mMessage.get( FINAL_FRAGMENT_FLAG ) ) - { - sb.append( " RESENDING" ); - } - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - sb.append( mMessage.toString() ); - - return sb.toString(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java deleted file mode 100644 index 11ed37b6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java +++ /dev/null @@ -1,242 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.module.decode.p25.reference.MDPConfigurationOption; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SNDCPActivateTDSContextAccept extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( SNDCPActivateTDSContextAccept.class ); - - /* SN-Activate TDS Context Accept */ - public static final int[] NSAPI = { 180,181,182,183 }; - public static final int[] PDUPM = { 184,185,186,187 }; - public static final int[] READY = { 188,189,190,191 }; - public static final int[] STANDBY = { 192,193,194,195 }; - public static final int[] NAT = { 196,197,198,199 }; - public static final int[] IP_1 = { 200,201,202,203,204,205,206,207 }; - public static final int[] IP_2 = { 208,209,210,211,212,213,214,215 }; - public static final int[] IP_3 = { 216,217,218,219,220,221,222,223 }; - public static final int[] IP_4 = { 224,225,226,227,228,229,230,231 }; - public static final int[] IPHC = { 232,233,234,235,236,237,238,239 }; - public static final int[] TCPSS = { 240,241,242,243 }; - public static final int[] UDPSS = { 244,245,246,247 }; - public static final int[] MTU = { 248,249,250,251 }; - public static final int[] UDPC = { 252,253,254,255 }; - public static final int[] MDPCO = { 256,257,258,259,260,261,262,263 }; - public static final int[] DATA_ACCESS_CONTROL = { 264,265,266,267,268, - 269,270,271,272,273,274,275,276,277,278,279 }; - - public SNDCPActivateTDSContextAccept( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " ACCEPT SNDCP PACKET DATA ACTIVATE " ); - sb.append( getNetworkAddressType() ); - sb.append( " " ); - sb.append( getIPAddress() ); - sb.append( " NSAPI:" ); - sb.append( getNSAPI() ); - sb.append( " MTU:" ); - sb.append( getMaximumTransmissionUnit() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - return sb.toString(); - } - - /** - * Network Service Access Point Identifier - up to 14 NSAPI's can be - * allocated to the mobile with each NSAPI to be used for a specific - * protocol layer. - */ - public int getNSAPI() - { - return mMessage.getInt( NSAPI ); - } - - public int getPDUPriorityMaximum() - { - return mMessage.getInt( PDUPM ); - } - - public String getReadyTimer() - { - switch( mMessage.getInt( READY ) ) - { - case 1: - return "1 Second"; - case 2: - return "2 Seconds"; - case 3: - return "4 Seconds"; - case 4: - return "6 Seconds"; - case 5: - return "8 Seconds"; - case 6: - return "10 Seconds"; - case 7: - return "15 Seconds"; - case 8: - return "20 Seconds"; - case 9: - return "25 Seconds"; - case 10: - return "30 Seconds"; - case 11: - return "60 Seconds"; - case 12: - return "120 Seconds"; - case 13: - return "180 Seconds"; - case 14: - return "300 Seconds"; - case 15: - return "Always in Ready"; - } - - return "UNKNOWN"; - } - - - public String getStandbyTimer() - { - switch( mMessage.getInt( STANDBY ) ) - { - case 1: - return "10 Seconds"; - case 2: - return "30 Seconds"; - case 3: - return "1 Minutes"; - case 4: - return "5 Minutes"; - case 5: - return "10 Minutes"; - case 6: - return "30 Minutes"; - case 7: - return "1 Hour"; - case 8: - return "2 Hours"; - case 9: - return "4 Hours"; - case 10: - return "8 Hours"; - case 11: - return "12 Hours"; - case 12: - return "24 Hours"; - case 13: - return "48 Hours"; - case 14: - return "72 Hours"; - case 15: - return "Always in Ready"; - } - - return "Unknown"; - } - - public String getNetworkAddressType() - { - return mMessage.getInt( NAT ) == 0 ? "IPV4 STATIC" : "IPV4 DYNAMIC"; - } - - public String getIPAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getInt( IP_1 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( IP_2 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( IP_3 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( IP_4 ) ); - - return sb.toString(); - } - - public boolean hasIPHeaderCompression() - { - return mMessage.getInt( IPHC ) == 1; - } - - public boolean hasUserDataPayloadCompression() - { - return mMessage.getInt( UDPC ) == 1; - } - - public int getTCPStateSlots() - { - return mMessage.getInt( TCPSS ); - } - - public int getUDPStateSlots() - { - return mMessage.getInt( UDPSS ); - } - - public String getMaximumTransmissionUnit() - { - int mtu = mMessage.getInt( MTU ); - - switch( mtu ) - { - case 1: - return "296 BYTES"; - case 2: - return "510 BYTES"; - case 3: - return "1020 BYTES"; - case 4: - return "1500 BYTES"; - default: - return "UNK-" + mtu; - } - } - - public MDPConfigurationOption getMDPConfigurationOption() - { - return MDPConfigurationOption.fromValue( mMessage.getInt( MDPCO ) ); - } - - public String getDataAccessControl() - { - return mMessage.getHex( DATA_ACCESS_CONTROL, 4 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java deleted file mode 100644 index ecc52b37..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.module.decode.p25.reference.SNDCPActivationRejectReason; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SNDCPActivateTDSContextReject extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( SNDCPActivateTDSContextReject.class ); - - public static final int[] NSAPI = { 180,181,182,183 }; - public static final int[] REJECT_CODE = { 184,185,186,187,188,189,190,191 }; - - public SNDCPActivateTDSContextReject( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " REJECT SNDCP PACKET DATA ACTIVATE - REASON:" ); - sb.append( getReason().getLabel() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "] " ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - /** - * Network Service Access Point Identifier - up to 14 NSAPI's can be - * allocated to the mobile with each NSAPI to be used for a specific - * protocol layer. - */ - public int getNSAPI() - { - return mMessage.getInt( NSAPI ); - } - - public SNDCPActivationRejectReason getReason() - { - return SNDCPActivationRejectReason.fromValue( mMessage.getInt( REJECT_CODE ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java deleted file mode 100644 index ea6e53bd..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java +++ /dev/null @@ -1,148 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.module.decode.p25.reference.MDPConfigurationOption; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SNDCPActivateTDSContextRequest extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( SNDCPActivateTDSContextRequest.class ); - - public static final int[] SNDCP_VERSION = { 180,181,182,183 }; - public static final int[] NSAPI = { 184,185,186,187 }; - public static final int[] NAT = { 188,189,190,191 }; - public static final int[] IP_1 = { 192,193,194,195,196,197,198,199 }; - public static final int[] IP_2 = { 200,201,202,203,204,205,206,207 }; - public static final int[] IP_3 = { 208,209,210,211,212,213,214,215 }; - public static final int[] IP_4 = { 216,217,218,219,220,221,222,223 }; - public static final int[] DSUT = { 224,225,226,227 }; - public static final int[] UDPC = { 228,229,230,231 }; - public static final int[] IPHC = { 232,233,234,235,236,237,238,239 }; - public static final int[] TCPSS = { 240,241,242,243 }; - public static final int[] UDPSS = { 244,245,246,247 }; - public static final int[] MDPCO = { 248,249,250,251,252,253,254,255 }; - - public SNDCPActivateTDSContextRequest( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " REQUEST SNDCP PACKET DATA ACTIVATE " ); - sb.append( getNetworkAddressType() ); - sb.append( " " ); - sb.append( getIPAddress() ); - sb.append( " NSAPI:" ); - sb.append( getNSAPI() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - return sb.toString(); - } - - /** - * SNDCP Version: 1 = P25 SNDCP Version 1 - */ - public int getSNDCPVersion() - { - return mMessage.getInt( SNDCP_VERSION ); - } - - /** - * Network Service Access Point Identifier - up to 14 NSAPI's can be - * allocated to the mobile with each NSAPI to be used for a specific - * protocol layer. - */ - public int getNSAPI() - { - return mMessage.getInt( NSAPI ); - } - - public String getNetworkAddressType() - { - return mMessage.getInt( NAT ) == 0 ? "IPV4 STATIC" : "IPV4 DYNAMIC"; - } - - public String getIPAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getInt( IP_1 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( IP_2 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( IP_3 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( IP_4 ) ); - - return sb.toString(); - } - - public String getDataSubscriberUnitType() - { - switch( mMessage.getInt( DSUT ) ) - { - case 0: - return "DATA ONLY MRC"; - case 1: - return "DATA AND VOICE MRC"; - } - - return "UNKNOWN"; - } - - public boolean hasIPHeaderCompression() - { - return mMessage.getInt( IPHC ) == 1; - } - - public boolean hasUserDataPayloadCompression() - { - return mMessage.getInt( UDPC ) == 1; - } - - public int getTCPStateSlots() - { - return mMessage.getInt( TCPSS ); - } - - public int getUDPStateSlots() - { - return mMessage.getInt( UDPSS ); - } - - public MDPConfigurationOption getMDPConfigurationOption() - { - return MDPConfigurationOption.fromValue( mMessage.getInt( MDPCO ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java deleted file mode 100644 index 62310317..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.module.decode.p25.reference.PDUType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SNDCPDeactivateTDSContext extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( SNDCPDeactivateTDSContext.class ); - - public static final int[] NSAPI = { 180,181,182,183 }; - public static final int[] DEACTIVATION_TYPE = { 184,185,186,187,188,189, - 190,191 }; - - public SNDCPDeactivateTDSContext( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " REQUEST SNDCP PACKET DATA DEACTIVATE " ); - sb.append( getDeactivationType() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "] " ); - - return sb.toString(); - } - - /** - * Network Service Access Point Identifier - up to 14 NSAPI's can be - * allocated to the mobile with each NSAPI to be used for a specific - * protocol layer. - */ - public int getNSAPI() - { - return mMessage.getInt( NSAPI ); - } - - public String getDeactivationType() - { - if( getPDUType() == PDUType.SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST ) - { - return mMessage.getInt( DEACTIVATION_TYPE ) == 0 ? - "ALL NSAPIS" : "THIS NSAPI"; - } - - return null; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java deleted file mode 100644 index 5ba57e42..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java +++ /dev/null @@ -1,355 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.confirmed; - -import io.github.dsheirer.module.decode.p25.reference.IPHeaderCompression; -import io.github.dsheirer.module.decode.p25.reference.IPProtocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SNDCPUserData extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( SNDCPUserData.class ); - - public static final int DATA_BLOCK_START = 176; - - public static final int[] NSAPI = { 180,181,182,183 }; - public static final int[] PCOMP = { 184,185,186,187 }; - public static final int[] DCOMP = { 188,189,190,191 }; - - /* IP Packet Header */ - public static final int[] IP_VERSION = { 192,193,194,195 }; - public static final int[] IHL = { 196,197,198,199 }; - public static final int[] DSCP = { 200,201,202,203,204,205 }; - public static final int[] ECN = { 206,207 }; - public static final int[] TOTAL_LENGTH = { 208,209,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223 }; - public static final int[] IDENTIFICATION = { 224,225,226,227,228,229,230,231, - 232,233,234,235,236,237,238,239 }; - public static final int[] FLAGS = { 240,241,242 }; - public static final int[] FRAGMENT_OFFSET = { 243,244,245,246,247,248,249, - 250,251,252,253,254,255 }; - public static final int[] TTL = { 256,257,258,259,260,261,262,263 }; - public static final int[] PROTOCOL = { 264,265,266,267,268,269,270,271 }; - public static final int[] HEADER_CHECKSUM = { 272,273,274,275,276,277,278, - 279,280,281,282,283,284,285,286,287 }; - public static final int[] SOURCE_IP_1 = { 288,289,290,291,292,293,294,295 }; - public static final int[] SOURCE_IP_2 = { 296,297,298,299,300,301,302,303 }; - public static final int[] BLOCK_SERIAL_2 = { 304,305,306,307,308,309,310 }; - public static final int[] BLOCK_CRC9_2 = { 311,312,313,314,315,316,317,318, - 319 }; - public static final int[] SOURCE_IP_3 = { 320,321,322,323,324,325,326,327 }; - public static final int[] SOURCE_IP_4 = { 328,329,330,331,332,333,334,335 }; - public static final int[] DESTINATION_IP_1 = { 336,337,338,339,340,341,342,343 }; - public static final int[] DESTINATION_IP_2 = { 344,345,346,347,348,349,350,351 }; - public static final int[] DESTINATION_IP_3 = { 352,353,354,355,356,357,358,359 }; - public static final int[] DESTINATION_IP_4 = { 360,361,362,363,364,365,366,367 }; - - public static final int IP_DATA_START = 368; - - /* User Datagram Protocol */ - public static final int[] UDP_SOURCE_PORT = { 368,369,370,371,372,373,374, - 375 }; - public static final int[] UDP_DESTINATION_PORT = { 376,377,378,379,380,381, - 382,383 }; - public static final int[] UDP_LENGTH = { 384,385,386,387,388,389,390,391, - 392,393,394,395,396,397,398,399 }; - public static final int[] UDP_CHECKSUM = { 400,401,402,403,404,405,406,407, - 408,409,410,411,412,413,414,415 }; - public static final int UDP_DATA_START = 416; - - public SNDCPUserData( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " SNDCP NSAPI:" ); - sb.append( getNSAPI() ); - sb.append( " " ); - IPProtocol protocol = getIPProtocol(); - - sb.append( protocol.getLabel() ); - sb.append( "/IP" ); - - sb.append( " FM:" ); - sb.append( getSourceIPAddress() ); - - if( protocol == IPProtocol.UDP ) - { - sb.append( ":" ); - sb.append( getUDPSourcePort() ); - } - - sb.append( " TO:" ); - sb.append( getDestinationIPAddress() ); - - if( protocol == IPProtocol.UDP ) - { - sb.append( ":" ); - sb.append( getUDPDestinationPort() ); - } - - sb.append( " DATA:" ); - sb.append( getPayload() ); - - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - /** - * Network Service Access Point Identifier - up to 14 NSAPI's can be - * allocated to the mobile with each NSAPI to be used for a specific - * protocol layer. - */ - public int getNSAPI() - { - return mMessage.getInt( NSAPI ); - } - - public IPHeaderCompression getIPHeaderCompressionState() - { - return IPHeaderCompression.fromValue( mMessage.getInt( PCOMP ) ); - } - - /** - * No enum values were defined for this - */ - public boolean hasUserDataCompression() - { - return mMessage.getInt( DCOMP ) > 0; - } - - /** - * IP Version - should always be 4 (IPV4) - */ - public int getIPVersion() - { - return mMessage.getInt( IP_VERSION ); - } - - /** - * IP packet header length in 32-bit words - */ - public int getInternetHeaderLength() - { - return mMessage.getInt( IHL ); - } - - /** - * IP packet DSCP - */ - public int getDifferentiatedServicesCodePoint() - { - return mMessage.getInt( DSCP ); - } - - /** - * IP packet ECN - */ - public int getExplicitCongestionNotification() - { - return mMessage.getInt( ECN ); - } - - /** - * IP packet size in bytes, including header and data - */ - public int getTotalLength() - { - return mMessage.getInt( TOTAL_LENGTH ); - } - - /** - * IP packet identification - */ - public int getIdentification() - { - return mMessage.getInt( IDENTIFICATION ); - } - - /** - * IP packet flags. 0-Reserved, 1-Don't Fragment, 2-More Fragments - */ - public int getFlags() - { - return mMessage.getInt( FLAGS ); - } - - public boolean isFragment() - { - return getFlags() > 0; - } - - /** - * IP packet - identifies the offset value in 8-byte blocks (64-bit blocks) - * for this packet fragment from the beginning. - */ - public int getFragmentOffset() - { - return mMessage.getInt( FRAGMENT_OFFSET ); - } - - /** - * IP packet time (hops) to live - */ - public int getTimeToLive() - { - return mMessage.getInt( TTL ); - } - - /** - * Identifies the IP protocol carried by the IP packet - */ - public IPProtocol getIPProtocol() - { - return IPProtocol.fromValue( mMessage.getInt( PROTOCOL ) ); - } - - /** - * Source IPV4 address - */ - public String getSourceIPAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getInt( SOURCE_IP_1 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( SOURCE_IP_2 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( SOURCE_IP_3 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( SOURCE_IP_4 ) ); - - return sb.toString(); - } - - /** - * Destination IPV4 address - */ - public String getDestinationIPAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getInt( DESTINATION_IP_1 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( DESTINATION_IP_2 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( DESTINATION_IP_3 ) ); - sb.append( "." ); - sb.append( mMessage.getInt( DESTINATION_IP_4 ) ); - - return sb.toString(); - } - - /** - * UDP source port - */ - public int getUDPSourcePort() - { - return mMessage.getInt( UDP_SOURCE_PORT ); - } - - /** - * UDP destination port - */ - public int getUDPDestinationPort() - { - return mMessage.getInt( UDP_DESTINATION_PORT ); - } - - /** - * UDP data payload size in bytes - */ - public int getUDPDataLength() - { - return mMessage.getInt( UDP_LENGTH ); - } - /** - * Hex string representation of the packet data, including packet header - * data if present. - */ - public String getPayload() - { - StringBuilder sb = new StringBuilder(); - - /* Adjust for PDU header (2 bytes) plus IP Packet header (20 bytes) */ - int start = 22; - - if( getIPProtocol() == IPProtocol.UDP ) - { - /* Adjust for UDP header (8 bytes) */ - start = 30; - } - - int spacingCounter = 0; - - /* Append octets inserting a space between each 32-bit value */ - int octets = getOctetCount() + getDataHeaderOffset(); - - for( int x = start; x < octets; x++ ) - { - sb.append( getPacketOctet( x ) ); - - spacingCounter++; - - if( spacingCounter >= 4 ) - { - sb.append( " " ); - spacingCounter = 0; - } - } - - return sb.toString(); - } - - /** - * Returns the octet identified by the 0-indexed 'octet' argument location. - * If there is a Data Header Offset, the 0 index points to the first octet - * of the data header. Otherwise, the 0 index points to the first octet - * of the packet. You must account for the data header offset when - * specifying the octet. - */ - public String getPacketOctet( int octet ) - { - int block = (int)( octet / 16 ); - int offset = octet % 16; - - int start = DATA_BLOCK_START + ( block * 144 ) + ( offset * 8 ); - - return mMessage.getHex( start, start + 7, 2 ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java deleted file mode 100644 index 661c2f64..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java +++ /dev/null @@ -1,208 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class AdjacentStatusBroadcastExtended extends PDUMessage implements IdentifierReceiver, IAdjacentSite -{ - public static final int[] LRA = {88, 89, 90, 91, 92, 93, 94, 95}; - public static final int[] SYSTEM_ID = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] RF_SUBSYSTEM_ID = {128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SITE_ID = {136, 137, 138, 139, 140, 141, 142, 143}; - public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; - public static final int[] TRANSMIT_CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}; - public static final int[] RECEIVE_IDENTIFIER = {176, 177, 178, 179}; - public static final int[] RECEIVE_CHANNEL = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; - public static final int[] SYSTEM_SERVICE_CLASS = {192, 193, 194, 195, 196, 197, 198, 199}; - public static final int[] MULTIPLE_BLOCK_CRC = {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}; - - private IBandIdentifier mTransmitIdentifierProvider; - private IBandIdentifier mReceiveIdentifierProvider; - - public AdjacentStatusBroadcastExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.ADJACENT_STATUS_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLRA()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" RFSS:" + getRFSS()); - - sb.append(" SITE:" + getSiteID()); - - sb.append(" CTRL CHAN:" + getTransmitIdentifier() + "-" + getTransmitChannel()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" " + getReceiveIdentifier() + "-" + getReceiveChannel()); - - sb.append(" UP:" + getUplinkFrequency()); - - sb.append(" SYS SVC CLASS:" + getSystemServiceClass()); - - return sb.toString(); - } - - public String getUniqueID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getSystemID()); - sb.append(":"); - sb.append(getRFSS()); - sb.append(":"); - sb.append(getSiteID()); - - return sb.toString(); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannel() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannel() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierProvider != null) - { - return mTransmitIdentifierProvider.getDownlinkFrequency(getTransmitChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierProvider != null) - { - return mReceiveIdentifierProvider.getUplinkFrequency(getReceiveChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierProvider = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierProvider = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitIdentifier(); - idens[1] = getReceiveIdentifier(); - - return idens; - } - - @Override - public String getRFSS() - { - return mMessage.getHex(RF_SUBSYSTEM_ID, 2); - } - - @Override - public String getLRA() - { - return mMessage.getHex(LRA, 2); - } - - @Override - public String getSystemServiceClass() - { - return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); - } - - @Override - public String getDownlinkChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannel(); - } - - @Override - public String getUplinkChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannel(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java deleted file mode 100644 index c212df94..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class CallAlertExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] WACN = { 128,129,130,131,132,133,134,135,136,137, - 138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SYSTEM_ID = { 164,165,166,167,168,169,170,171,172, - 173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 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 }; - - public CallAlertExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.CALL_ALERT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " WACN:" ); - sb.append( getWACN() ); - - sb.append( " SYS:" ); - sb.append( getSystemID() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java deleted file mode 100644 index f4d203ae..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupAffiliationQueryExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] WACN = { 128,129,130,131,132,133,134,135,136,137, - 138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SYSTEM_ID = { 164,165,166,167,168,169,170,171,172, - 173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 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 }; - - public GroupAffiliationQueryExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_AFFILIATION_QUERY.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " WACN:" ); - sb.append( getWACN() ); - - sb.append( " SYS:" ); - sb.append( getSystemID() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java deleted file mode 100644 index 42d95566..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.Response; - -public class GroupAffiliationResponseExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SOURCE_WACN = { 128,129,130,131,132,133,134,135, - 136,137,138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SOURCE_SYSTEM_ID = { 164,165,166,167,168,169,170, - 171,172,173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] GROUP_WACN = { 200,201,202,203,204,205,206,207, - 208,209,210,211,212,213,214,215,216,217,218,219 }; - public static final int[] GROUP_SYSTEM_ID = { 220,221,222,223,224,225,226, - 227,228,229,230,231 }; - public static final int[] GROUP_ID = { 232,233,234,235,236,237,238,239,240, - 241,242,243,244,245,246,247 }; - public static final int[] ANNOUNCEMENT_GROUP_ID = { 248,249,250,251,252,253, - 254,255,256,257,258,259,260,261,262,263 }; - public static final int[] GROUP_AFFILIATION_RESPONSE_VALUE = { 270,271 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, - 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, - 343,344,345,346,347,348,349,350,351 }; - - public GroupAffiliationResponseExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_AFFILIATION_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( "AFFILIATION:" ); - sb.append( getResponse().name() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " WACN:" ); - sb.append( getSourceWACN() ); - - sb.append( " SYS:" ); - sb.append( getSourceSystemID() ); - - sb.append( " GROUP:" ); - sb.append( getGroupID() ); - - sb.append( " WACN:" ); - sb.append( getGroupWACN() ); - - sb.append( " SYS:" ); - sb.append( getGroupSystemID() ); - - sb.append( " ANNOUNCEMENT GROUP:" ); - sb.append( getAnnouncementGroupID() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceWACN() - { - return mMessage.getHex( SOURCE_WACN, 5 ); - } - - public String getSourceSystemID() - { - return mMessage.getHex( SOURCE_SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getGroupWACN() - { - return mMessage.getHex( GROUP_WACN, 5 ); - } - - public String getGroupSystemID() - { - return mMessage.getHex( GROUP_SYSTEM_ID, 3 ); - } - - public String getGroupID() - { - return mMessage.getHex( GROUP_ID, 4 ); - } - - public String getAnnouncementGroupID() - { - return mMessage.getHex( GROUP_ID, 4 ); - } - - public Response getResponse() - { - return Response.fromValue( - mMessage.getInt( GROUP_AFFILIATION_RESPONSE_VALUE ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java deleted file mode 100644 index 44a8e6d5..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class MessageUpdateExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SOURCE_WACN = { 128,129,130,131,132,133,134,135,136,137, - 138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SOURCE_SYSTEM_ID = { 164,165,166,167,168,169,170,171,172, - 173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] SHORT_DATA_MESSAGE = { 200,201,202,203,204,205, - 206,207,208,209,210,211,212,213,214,215 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 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 }; - - public MessageUpdateExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.CALL_ALERT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " MSG:" ); - sb.append( getShortDataMessage() ); - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " WACN:" ); - sb.append( getSourceWACN() ); - - sb.append( " SYS:" ); - sb.append( getSourceSystemID() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceWACN() - { - return mMessage.getHex( SOURCE_WACN, 5 ); - } - - public String getSourceSystemID() - { - return mMessage.getHex( SOURCE_SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getShortDataMessage() - { - return mMessage.getHex( SHORT_DATA_MESSAGE, 4 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java deleted file mode 100644 index 1e17466a..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.P25NetworkCallsign; - -public class NetworkStatusBroadcastExtended extends PDUMessage implements IdentifierReceiver -{ - public static final int[] LRA = {88, 89, 90, 91, 92, 93, 94, 95}; - public static final int[] SYSTEM_ID = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] WACN = {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179}; - public static final int[] TRANSMIT_IDENTIFIER = {184, 185, 186, 187}; - public static final int[] TRANSMIT_CHANNEL = {188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199}; - public static final int[] RECEIVE_IDENTIFIER = {200, 201, 202, 203}; - public static final int[] RECEIVE_CHANNEL = {204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215}; - public static final int[] SYSTEM_SERVICE_CLASS = {216, 217, 218, 219, 220, 221, 222, 223}; - public static final int[] MULTIPLE_BLOCK_CRC = {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}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public NetworkStatusBroadcastExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.NETWORK_STATUS_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" WACN:" + getWACN()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" CTRL CHAN:" + getReceiveIdentifier() + "-" + getReceiveChannel()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" " + getTransmitIdentifier() + "-" + getTransmitChannel()); - - sb.append(" UP:" + getUplinkFrequency()); - - sb.append(" SYS SVC CLASS:" + - SystemService.toString(getSystemServiceClass())); - - return sb.toString(); - } - - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LRA, 2); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getWACN() - { - return mMessage.getHex(WACN, 5); - } - - public String getNetworkCallsign() - { - return P25NetworkCallsign.getCallsign(mMessage.getInt(WACN), - mMessage.getInt(SYSTEM_ID)); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannel() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannel() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitIdentifier(); - idens[1] = getReceiveIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java deleted file mode 100644 index e0325000..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java +++ /dev/null @@ -1,113 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Encryption; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class ProtectionParameterBroadcast extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] ENCRYPTION_TYPE = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] ALGORITHM_ID = { 136,137,138,139,140,141,142,143 }; - public static final int[] KEY_ID = { 160,161,162,163,164,165,166,167,168, - 169,170,171,172,173,174,175 }; - public static final int[] INBOUND_INITIALIZATION_VECTOR_A = { 176,177,178, - 179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196, - 197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214, - 215 }; - public static final int[] INBOUND_INITIALIZATION_VECTOR_B = { 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 }; - public static final int[] OUTBOUND_INITIALIZATION_VECTOR_A = { 248,249,250, - 251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268, - 269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286, - 287 }; - public static final int[] OUTBOUND_INITIALIZATION_VECTOR_B = { 288,289,290, - 291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308, - 309,310,311,312,313,314,315,316,317,318,319 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, - 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, - 343,344,345,346,347,348,349,350,351 }; - - public ProtectionParameterBroadcast(BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.PROTECTION_PARAMETER_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " ENCRYPTION:" ); - sb.append( getEncryptionType().name() ); - - sb.append( " ALGORITHM ID:" ); - sb.append( getAlgorithmID() ); - - sb.append( " KEY ID:" ); - sb.append( getKeyID() ); - - sb.append( " INBOUND IV:" ); - sb.append( getInboundInitializationVector() ); - - sb.append( " OUTBOUND IV:" ); - sb.append( getOutboundInitializationVector() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public Encryption getEncryptionType() - { - return Encryption.fromValue( mMessage.getInt( ENCRYPTION_TYPE ) ); - } - - public String getAlgorithmID() - { - return mMessage.getHex( ALGORITHM_ID, 2 ); - } - - public String getKeyID() - { - return mMessage.getHex( KEY_ID, 4 ); - } - - public String getInboundInitializationVector() - { - return mMessage.getHex( INBOUND_INITIALIZATION_VECTOR_A, 10 ) + - mMessage.getHex( INBOUND_INITIALIZATION_VECTOR_B, 8 ); - } - - public String getOutboundInitializationVector() - { - return mMessage.getHex( OUTBOUND_INITIALIZATION_VECTOR_A, 10 ) + - mMessage.getHex( OUTBOUND_INITIALIZATION_VECTOR_B, 8 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java deleted file mode 100644 index b97ecb66..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class RFSSStatusBroadcastExtended extends PDUMessage implements IdentifierReceiver -{ - public static final int[] LRA = {88, 89, 90, 91, 92, 93, 94, 95}; - public static final int ACTIVE_NETWORK_CONNECTION_INDICATOR = 99; - public static final int[] SYSTEM_ID = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] RFSS_ID = {160, 161, 162, 163, 164, 165, 166, 167}; - public static final int[] SITE_ID = {168, 169, 170, 171, 172, 173, 174, 175}; - public static final int[] TRANSMIT_IDENTIFIER = {184, 185, 186, 187}; - public static final int[] TRANSMIT_CHANNEL = {188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199}; - public static final int[] RECEIVE_IDENTIFIER = {200, 201, 202, 203}; - public static final int[] RECEIVE_CHANNEL = {204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215}; - public static final int[] SYSTEM_SERVICE_CLASS = {216, 217, 218, 219, 220, 221, 222, 223}; - public static final int[] MULTIPLE_BLOCK_CRC = {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}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public RFSSStatusBroadcastExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.NETWORK_STATUS_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" CTRL CHAN:" + getReceiveIdentifier() + "-" + getReceiveChannel()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" " + getTransmitIdentifier() + "-" + getTransmitChannel()); - - sb.append(" UP:" + getUplinkFrequency()); - - sb.append(" SYS SVC CLASS:" + - SystemService.toString(getSystemServiceClass())); - - return sb.toString(); - } - - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LRA, 2); - } - - public boolean hasActiveNetworkConnection() - { - return mMessage.get(ACTIVE_NETWORK_CONNECTION_INDICATOR); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannel() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannel() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitIdentifier(); - idens[1] = getReceiveIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java deleted file mode 100644 index c60184e0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java +++ /dev/null @@ -1,269 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.P25MessageFramer; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class RoamingAddressUpdateExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int LAST_MESSAGE_INDICATOR = 128; - public static final int[] MESSAGE_SEQUENCE_NUMBER = { 132,133,134,135 }; - public static final int[] WACN_A = { 136,137,138,139,140,141,142,143,160, - 161,162,163,164,165,166,167,168,169,170,171 }; - public static final int[] SYSTEM_ID_A = { 172,173,174,175,176,177,178,179, - 180,181,182,183 }; - public static final int[] SOURCE_ID_FORMAT_1 = { 184,185,186,187,188,189, - 190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207 }; - public static final int[] WACN_B = { 184,185,186,187,188,189,190,191,192, - 193,194,195,196,197,198,199,200,201,202,203 }; - public static final int[] SYSTEM_ID_B = { 204,205,206,207,208,209,210,211, - 212,213,214,215 }; - public static final int[] WACN_C = { 216,217,218,219,220,221,222,223,224, - 225,226,227,228,229,230,231,232,233,234,235 }; - public static final int[] SYSTEM_ID_C = { 236,237,238,239,240,241,242,243, - 244,245,246,247 }; - public static final int[] WACN_D = { 248,249,250,251,252,253,254,255,256, - 257,258,259,260,261,262,263,264,265,266,267 }; - public static final int[] SYSTEM_ID_D = { 268,269,270,271,272,273,274,275, - 276,277,278,279 }; - public static final int[] SOURCE_ID_FORMAT_2 = { 280,281,282,283,284,285, - 286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303 }; - public static final int[] WACN_E = { 280,281,282,283,284,285, - 286,287,288,289,290,291,292,293,294,295,296,297,298,299 }; - public static final int[] SYSTEM_ID_E = { 300,301,302,303,304,305,306,307, - 308,309,310,311 }; - public static final int[] WACN_F = { 312,313,314,315,316,317,318,319,320, - 321,322,323,324,325,326,327,328,329,330,331 }; - public static final int[] SYSTEM_ID_F = { 332,333,334,335,336,337,338,339, - 340,341,342,343 }; - public static final int[] WACN_G = { 344,345,346,347,348,349,350,351,352, - 353,354,355,356,357,358,359,360,361,362,363 }; - public static final int[] SYSTEM_ID_G = { 364,365,366,367,368,369,370,371, - 372,373,374,375 }; - public static final int[] SOURCE_ID_FORMAT_3 = { 376,377,378,379,380,381, - 382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399 }; - public static final int[] MULTIPLE_BLOCK_CRC_FORMAT_1 = { 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 }; - public static final int[] MULTIPLE_BLOCK_CRC_FORMAT_2 = { 320,321,322,323, - 324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341, - 342,343,344,345,346,347,348,349,350,351 }; - public static final int[] MULTIPLE_BLOCK_CRC_FORMAT_3 = { 416,417,418,419, - 420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437, - 438,439,440,441,442,443,444,445,446,447 }; - - private enum Format{ FORMAT_1, FORMAT_2, FORMAT_3 }; - - private Format mFormat; - - public RoamingAddressUpdateExtended(BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - if( mMessage.size() == P25MessageFramer.PDU2_BEGIN ) - { - mFormat = Format.FORMAT_1; - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - else if( mMessage.size() == P25MessageFramer.PDU3_BEGIN ) - { - mFormat = Format.FORMAT_2; - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU2( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - else if( mMessage.size() == P25MessageFramer.PDU3_DECODED_END ) - { - mFormat = Format.FORMAT_3; - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU3( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - else - { - mFormat = Format.FORMAT_1; - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - } - - @Override - public String getEventType() - { - return Opcode.ROAMING_ADDRESS_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ROAMING ADDRESS STACK FROM:" ); - sb.append( getSourceID() ); - - if( isLastBlock() ) - { - sb.append( " LAST BLOCK" ); - } - - sb.append( "MSG SEQ:" ); - sb.append( getMessageSequenceNumber() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " WACN A:" + getWACNA() ); - sb.append( " SYS A:" + getSystemIDA() ); - - if( isFormat2() ) - { - sb.append( " WACN B:" + getWACNB() ); - sb.append( " SYS B:" + getSystemIDB() ); - sb.append( " WACN C:" + getWACNC() ); - sb.append( " SYS C:" + getSystemIDC() ); - sb.append( " WACN D:" + getWACND() ); - sb.append( " SYS D:" + getSystemIDD() ); - } - - if( isFormat3() ) - { - sb.append( " WACN E:" + getWACNE() ); - sb.append( " SYS E:" + getSystemIDE() ); - sb.append( " WACN F:" + getWACNF() ); - sb.append( " SYS F:" + getSystemIDF() ); - sb.append( " WACN G:" + getWACNG() ); - sb.append( " SYS G:" + getSystemIDG() ); - } - - return sb.toString(); - } - - public boolean isLastBlock() - { - return mMessage.get( LAST_MESSAGE_INDICATOR ); - } - - public int getMessageSequenceNumber() - { - return mMessage.getInt( MESSAGE_SEQUENCE_NUMBER ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getWACNA() - { - return mMessage.getHex( WACN_A, 5 ); - } - - public String getSystemIDA() - { - return mMessage.getHex( SYSTEM_ID_A, 3 ); - } - - public String getWACNB() - { - return isFormat2() ? mMessage.getHex( WACN_B, 5 ) : null; - } - - public String getSystemIDB() - { - return isFormat2() ? mMessage.getHex( SYSTEM_ID_B, 3 ) : null; - } - - public String getWACNC() - { - return isFormat2() ? mMessage.getHex( WACN_C, 5 ) : null; - } - - public String getSystemIDC() - { - return isFormat2() ? mMessage.getHex( SYSTEM_ID_C, 3 ) : null; - } - - public String getWACND() - { - return isFormat2() ? mMessage.getHex( WACN_D, 5 ) : null; - } - - public String getSystemIDD() - { - return isFormat2() ? mMessage.getHex( SYSTEM_ID_D, 3 ) : null; - } - - public String getWACNE() - { - return isFormat3() ? mMessage.getHex( WACN_E, 5 ) : null; - } - - public String getSystemIDE() - { - return isFormat3() ? mMessage.getHex( SYSTEM_ID_E, 3 ) : null; - } - - public String getWACNF() - { - return isFormat3() ? mMessage.getHex( WACN_F, 5 ) : null; - } - - public String getSystemIDF() - { - return isFormat3() ? mMessage.getHex( SYSTEM_ID_F, 3 ) : null; - } - - public String getWACNG() - { - return isFormat3() ? mMessage.getHex( WACN_G, 5 ) : null; - } - - public String getSystemIDG() - { - return isFormat3() ? mMessage.getHex( SYSTEM_ID_G, 3 ) : null; - } - - public String getSourceID() - { - switch( mFormat ) - { - case FORMAT_1: - return mMessage.getHex( SOURCE_ID_FORMAT_1, 6 ); - case FORMAT_2: - return mMessage.getHex( SOURCE_ID_FORMAT_2, 6 ); - case FORMAT_3: - return mMessage.getHex( SOURCE_ID_FORMAT_2, 6 ); - } - - return null; - } - - public boolean isFormat2() - { - return mFormat != Format.FORMAT_3; - } - - public boolean isFormat3() - { - return mFormat == Format.FORMAT_3; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java deleted file mode 100644 index df3a3dc0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class StatusQueryExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SOURCE_WACN = { 128,129,130,131,132,133,134,135,136,137, - 138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SOURCE_SYSTEM_ID = { 164,165,166,167,168,169,170,171,172, - 173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 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 }; - - public StatusQueryExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.STATUS_QUERY.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " WACN:" ); - sb.append( getSourceWACN() ); - - sb.append( " SYS:" ); - sb.append( getSourceSystemID() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceWACN() - { - return mMessage.getHex( SOURCE_WACN, 5 ); - } - - public String getSourceSystemID() - { - return mMessage.getHex( SOURCE_SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java deleted file mode 100644 index 280f9dfe..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class StatusUpdateExtended extends PDUMessage -{ - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SOURCE_WACN = { 128,129,130,131,132,133,134,135,136,137, - 138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SOURCE_SYSTEM_ID = { 164,165,166,167,168,169,170,171,172, - 173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] UNIT_STATUS = { 200,201,202,203,204,205,206,207 }; - public static final int[] USER_STATUS = { 208,209,210,211,212,213,214,215 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 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 }; - - public StatusUpdateExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.STATUS_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " WACN:" ); - sb.append( getSourceWACN() ); - - sb.append( " SYS:" ); - sb.append( getSourceSystemID() ); - - sb.append( " STATUS USER:" + getUserStatus() ); - sb.append( " UNIT:" + getUnitStatus() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceWACN() - { - return mMessage.getHex( SOURCE_WACN, 5 ); - } - - public String getSourceSystemID() - { - return mMessage.getHex( SOURCE_SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getUserStatus() - { - return mMessage.getHex( USER_STATUS, 2 ); - } - - public String getUnitStatus() - { - return mMessage.getHex( UNIT_STATUS, 2 ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java deleted file mode 100644 index b35a680b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.Response; - -public class UnitRegistrationResponseExtended extends PDUMessage -{ - public static final int[] ASSIGNED_SOURCE_ADDRESS = { 88,89,90,91,92, - 93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] WACN = { 128,129,130,131,132,133,134,135, - 136,137,138,139,140,141,142,143,160,161,162,163 }; - public static final int[] SYSTEM_ID = { 164,165,166,167,168,169,170, - 171,172,173,174,175 }; - public static final int[] SOURCE_ID = { 176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199 }; - public static final int[] SOURCE_ADDRESS = { 200,201,202,203,204,205,206, - 207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223 }; - public static final int[] UNIT_REGISTRATION_RESPONSE_VALUE = { 230,231 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, - 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, - 343,344,345,346,347,348,349,350,351 }; - - public UnitRegistrationResponseExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_REGISTRATION_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( "REGISTRATION:" ); - sb.append( getResponse().name() ); - - sb.append( " SRC ID:" ); - sb.append( getSourceID() ); - - sb.append( " ADDR:" ); - sb.append( getSourceAddress() ); - - sb.append( " WACN:" ); - sb.append( getWACN() ); - - sb.append( " SYS:" ); - sb.append( getSystemID() ); - - return sb.toString(); - } - - public String getAssignedSourceAddress() - { - return mMessage.getHex( ASSIGNED_SOURCE_ADDRESS, 6 ); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - public Response getResponse() - { - return Response.fromValue( - mMessage.getInt( UNIT_REGISTRATION_RESPONSE_VALUE ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java deleted file mode 100644 index 0913a06a..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupDataChannelGrantExtended extends PDUMessage implements IdentifierReceiver -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 128; - public static final int ENCRYPTED_CHANNEL_FLAG = 129; - public static final int DUPLEX_MODE = 130; - public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; - public static final int[] TRANSMIT_NUMBER = {164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}; - public static final int[] RECEIVE_IDENTIFIER = {176, 177, 178, 179}; - public static final int[] RECEIVE_NUMBER = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; - public static final int[] GROUP_ADDRESS = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, - 206, 207}; - public static final int[] MULTIPLE_BLOCK_CRC = {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}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupDataChannelGrantExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" FROM:"); - sb.append(getSourceAddress()); - - sb.append(" TO:"); - sb.append(getGroupAddress()); - - sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - sb.append(" " + getDownlinkFrequency()); - - sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - sb.append(" " + getUplinkFrequency()); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncrypted() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getSourceAddress() - { - return mMessage.getHex(SOURCE_ADDRESS, 6); - } - - public String getGroupAddress() - { - return mMessage.getHex(GROUP_ADDRESS, 4); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitChannelIdentifier(); - idens[1] = getReceiveChannelIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java deleted file mode 100644 index 512cb2a2..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.UnitToUnitChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class IndividualDataChannelGrantExtended extends UnitToUnitChannelGrantExtended implements IdentifierReceiver -{ - public IndividualDataChannelGrantExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.INDIVIDUAL_DATA_CHANNEL_GRANT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java deleted file mode 100644 index a5f64ad3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupVoiceChannelGrantExplicit extends PDUMessage implements IdentifierReceiver -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 128; - public static final int ENCRYPTED_CHANNEL_FLAG = 129; - public static final int DUPLEX_MODE = 130; - public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] TRANSMIT_IDENTIFIER = {176, 177, 178, 179}; - public static final int[] TRANSMIT_NUMBER = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; - public static final int[] RECEIVE_IDENTIFIER = {192, 193, 194, 195}; - public static final int[] RECEIVE_NUMBER = {196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207}; - public static final int[] GROUP_ADDRESS = {208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, - 222, 223}; - public static final int[] MULTIPLE_BLOCK_CRC = {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}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupVoiceChannelGrantExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_VOICE_CHANNEL_GRANT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" FROM:"); - sb.append(getSourceAddress()); - - sb.append(" GRP:"); - sb.append(getGroupAddress()); - - sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - sb.append(" " + getDownlinkFrequency()); - - sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - sb.append(" " + getUplinkFrequency()); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncrypted() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getSourceAddress() - { - return mMessage.getHex(SOURCE_ADDRESS, 6); - } - - public String getGroupAddress() - { - return mMessage.getHex(GROUP_ADDRESS, 4); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitChannelIdentifier(); - idens[1] = getReceiveChannelIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java deleted file mode 100644 index b3b3880d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java +++ /dev/null @@ -1,206 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -import java.util.Date; - -public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage implements IdentifierReceiver -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 128; - public static final int ENCRYPTED_CHANNEL_FLAG = 129; - public static final int DUPLEX_MODE = 130; - public static final int SESSION_MODE = 131; - - public static final int[] ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111}; - public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; - public static final int[] TRANSMIT_NUMBER = {164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}; - public static final int[] RECEIVE_IDENTIFIER = {176, 177, 178, 179}; - public static final int[] RECEIVE_NUMBER = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; - public static final int[] CALL_TIMER = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207}; - public static final int[] MULTIPLE_BLOCK_CRC = {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}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public TelephoneInterconnectChannelGrantExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" ADDR:"); - sb.append(getAddress()); - - sb.append(" CALL TIMER:"); - sb.append(mTimeDurationFormat.format(new Date(getCallTimer()))); - - sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - sb.append(" " + getDownlinkFrequency()); - - sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - sb.append(" " + getUplinkFrequency()); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncrypted() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public P25Message.DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? P25Message.DuplexMode.FULL : P25Message.DuplexMode.HALF; - } - - public P25Message.SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - P25Message.SessionMode.CIRCUIT : P25Message.SessionMode.PACKET; - } - - public String getAddress() - { - return mMessage.getHex(ADDRESS, 6); - } - - /* - * Call timer in milliseconds - */ - public long getCallTimer() - { - int timer = mMessage.getInt(CALL_TIMER); - - return timer / 100; - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitChannelIdentifier(); - idens[1] = getReceiveChannelIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java deleted file mode 100644 index 587565ec..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class TelephoneInterconnectChannelGrantUpdateExplicit - extends TelephoneInterconnectChannelGrantExplicit -{ - public TelephoneInterconnectChannelGrantUpdateExplicit(BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java deleted file mode 100644 index 39e028c3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitToUnitAnswerRequestExplicit extends PDUMessage -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 128; - public static final int ENCRYPTED_CHANNEL_FLAG = 129; - public static final int DUPLEX_MODE = 130; - public static final int SESSION_MODE = 131; - - public static final int[] TARGET_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] WACN = { 168,169,170,171,172,173,174,175,176,177, - 178,179,180,181,182,183,184,185,186,187 }; - public static final int[] SYSTEM_ID = { 188,189,190,191,192,193,194,195,196, - 197,198,199 }; - public static final int[] SOURCE_ID = { 200,201,202,203,204,205,206,207,208, - 209,210,211,212,213,214,215,216,217,218,219,220,221,222,223 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 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 }; - - public UnitToUnitAnswerRequestExplicit(BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_TO_UNIT_ANSWER_REQUEST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " FROM:" ); - sb.append( getSourceID() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " WACN:" + getWACN() ); - - sb.append( " SYS:" + getSystemID() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public P25Message.DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? P25Message.DuplexMode.FULL : P25Message.DuplexMode.HALF; - } - - public P25Message.SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - P25Message.SessionMode.CIRCUIT : P25Message.SessionMode.PACKET; - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java deleted file mode 100644 index 81d30f74..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.pdu.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.UnitToUnitChannelGrantExtended; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitToUnitVoiceChannelGrantExtended - extends UnitToUnitChannelGrantExtended - implements IdentifierReceiver -{ - public UnitToUnitVoiceChannelGrantExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java deleted file mode 100644 index d981c610..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java +++ /dev/null @@ -1,228 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.pdu.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRCP25; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage implements IdentifierReceiver -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 128; - public static final int ENCRYPTED_CHANNEL_FLAG = 129; - public static final int DUPLEX_MODE = 130; - public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SOURCE_WACN = {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, - 175, 176, 177, 178, 179}; - public static final int[] SOURCE_SYSTEM_ID = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; - public static final int[] SOURCE_ID = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, - 207, 208, 209, 210, 211, 212, 213, 214, 215}; - public static final int[] TARGET_ADDRESS = {216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, - 230, 231, 232, 233, 234, 235, 236, 237, 238, 239}; - public static final int[] TRANSMIT_IDENTIFIER = {240, 241, 242, 243}; - public static final int[] TRANSMIT_NUMBER = {244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; - public static final int[] RECEIVE_IDENTIFIER = {256, 257, 258, 259}; - public static final int[] RECEIVE_NUMBER = {260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271}; - public static final int[] MULTIPLE_BLOCK_CRC = {320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, - 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public UnitToUnitVoiceChannelGrantUpdateExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - - /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU2(mMessage); - mCRC[1] = mMessage.getCRC(); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" FROM ADDR:"); - sb.append(getSourceAddress()); - - sb.append(" ID:"); - sb.append(getSourceID()); - - sb.append(" TO:"); - sb.append(getTargetAddress()); - - sb.append(" WACN:"); - sb.append(getSourceWACN()); - - sb.append(" SYS:"); - sb.append(getSourceSystemID()); - - sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - sb.append(" " + getDownlinkFrequency()); - - sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - sb.append(" " + getUplinkFrequency()); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncrypted() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getSourceAddress() - { - return mMessage.getHex(SOURCE_ADDRESS, 6); - } - - public String getSourceID() - { - return mMessage.getHex(SOURCE_ID, 6); - } - - public String getSourceWACN() - { - return mMessage.getHex(SOURCE_WACN, 5); - } - - public String getSourceSystemID() - { - return mMessage.getHex(SOURCE_SYSTEM_ID, 3); - } - - public String getTargetAddress() - { - return mMessage.getHex(TARGET_ADDRESS, 6); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - public int[] getIdentifiers() - { - int[] idens = new int[2]; - - idens[0] = getTransmitChannelIdentifier(); - idens[1] = getReceiveChannelIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketHeader.java new file mode 100644 index 00000000..f2ae0131 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketHeader.java @@ -0,0 +1,128 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.pdu.packet; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUHeader; +import io.github.dsheirer.module.decode.p25.reference.ServiceAccessPoint; + +public class PacketHeader extends PDUHeader +{ + public static final int[] SAP_ID = {10, 11, 12, 13, 14, 15}; + public static final int FULL_MESSAGE_FLAG = 48; + public static final int[] PAD_OCTET_COUNT = {59, 60, 61, 62, 63}; + public static final int SYNCHRONIZE_FLAG = 64; + public static final int[] PACKET_SEQUENCE_NUMBER = {65,66}; + public static final int[] MESSAGE_FRAGMENT_SEQUENCE_NUMBER = {67,68,69}; + public static final int[] DATA_HEADER_OFFSET = {74, 75, 76, 77, 78, 79}; + + public PacketHeader(CorrectedBinaryMessage message, boolean passesCRC) + { + super(message, passesCRC); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append(" *CRC-FAIL*"); + } + + sb.append(isConfirmationRequired() ? " CONFIRMED" : " UNCONFIRMED"); + sb.append(" ").append(getDirection()); + sb.append(" FMT:").append(getFormat().getLabel()); + sb.append(" SAP:").append(getServiceAccessPoint().name()); + sb.append(" VEND:").append(getVendor().getLabel()); + sb.append(isOutbound() ? "TO" : "FROM").append(" LLID:").append(getLLID()); + sb.append(" BLKS TO FOLLOW:").append(getBlocksToFollowCount()); + + if(isSynchronize()) + { + sb.append(" PACKET:").append(getPacketSequenceNumber()); + sb.append(" MSG FRAGMENT:").append(getMessageFragmentSequenceNumber()); + } + + sb.append(" PAD OCTETS:").append(getPadOctetCount()); + sb.append(" HEADER OFFSET:").append(getDataHeaderOffset()); + + return sb.toString(); + } + + /** + * Service Access Point (SAP) - determines the network service that will process this packet + */ + public ServiceAccessPoint getServiceAccessPoint() + { + return ServiceAccessPoint.fromValue(getMessage().getInt(SAP_ID)); + } + + /** + * Indicates if this is the first time this packet header has been transmitted. A value of false + * indicates this is a retransmitted packet header. + */ + public boolean isFullMessage() + { + return getMessage().get(FULL_MESSAGE_FLAG); + } + + /** + * Number of octets (bytes) that are appended to the end of the packet to make a full final block + */ + public int getPadOctetCount() + { + return getMessage().getInt(PAD_OCTET_COUNT); + } + + /** + * Indicates that this header contains packet and message fragment sequence numbers + */ + public boolean isSynchronize() + { + return getMessage().get(SYNCHRONIZE_FLAG); + } + + /** + * Packet sequence number for correct ordering by the receiver. + */ + public int getPacketSequenceNumber() + { + return getMessage().getInt(PACKET_SEQUENCE_NUMBER); + } + + /** + * Message fragment sequence number for correct ordering by the receiver + */ + public int getMessageFragmentSequenceNumber() + { + return getMessage().getInt(MESSAGE_FRAGMENT_SEQUENCE_NUMBER); + } + + /** + * Number of bytes in the data portion of the packet that are dedicated to the header. This + * offset is used to identify the start of the data portion of the message. + */ + public int getDataHeaderOffset() + { + return getMessage().getInt(DATA_HEADER_OFFSET); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketMessage.java new file mode 100644 index 00000000..1252f355 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/PacketMessage.java @@ -0,0 +1,267 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet; + +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.PacketMessageFactory; +import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.block.ConfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.block.DataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacketHeader; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.IPHeaderCompression; +import io.github.dsheirer.module.decode.p25.reference.UDPHeaderCompression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Packet Data Unit (PDU) sequence containing IP packet data. + */ +public class PacketMessage extends P25Message +{ + private final static Logger mLog = LoggerFactory.getLogger(PacketMessage.class); + + private PDUSequence mPDUSequence; + private BinaryMessage mPayload; + private BinaryMessage mPacketMessage; + private SNDCPPacketHeader mSNDCPPacketHeader; + private IPacket mPacket; + + public PacketMessage(PDUSequence PDUSequence, int nac, long timestamp) + { + super(null, nac, timestamp); + mPDUSequence = PDUSequence; + } + + /** + * Packet Data Unit (PDU) header and data block(s) for this message + */ + public PDUSequence getPDUSequence() + { + return mPDUSequence; + } + + /** + * Packet header from the packet sequence. + */ + public PacketHeader getHeader() + { + return (PacketHeader)getPDUSequence().getHeader(); + } + + public SNDCPPacketHeader getSNDCPPacketHeader() + { + if(mSNDCPPacketHeader == null) + { + if(getPDUSequence().isComplete() && getHeader().getDataHeaderOffset() == 2) + { + if(getPDUSequence().getDataBlocks().size() >= 1) + { + BinaryMessage message = getPDUSequence().getDataBlocks().get(0).getMessage().getSubMessage(0, 16); + mSNDCPPacketHeader = new SNDCPPacketHeader(message, getHeader().isOutbound()); + } + else + { + mLog.debug("***************** 2 byte SNDCP header with no data blocks header: " + getPDUSequence().getHeader().getMessage().toHexString()); + mSNDCPPacketHeader = new SNDCPPacketHeader(getHeader().isOutbound()); + } + } + else + { + mSNDCPPacketHeader = new SNDCPPacketHeader(getHeader().isOutbound()); + } + } + + return mSNDCPPacketHeader; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(!getPDUSequence().isComplete()) + { + sb.append(" *INCOMPLETE - RECEIVED ").append(getPDUSequence().getDataBlocks().size()).append("/") + .append(getPDUSequence().getHeader().getBlocksToFollowCount()).append(" DATA BLOCKS"); + } + + if(getPacket() instanceof IPV4Packet) + { + sb.append(" LLID:").append(getHeader().getLLID()); + + SNDCPPacketHeader sndcpPacketHeader = getSNDCPPacketHeader(); + sb.append(" NSAPI:").append(sndcpPacketHeader.getNSAPI()); + + if(sndcpPacketHeader.getIPHeaderCompression() != IPHeaderCompression.NONE) + { + sb.append("IP HEADER COMPRESSION:").append(sndcpPacketHeader.getIPHeaderCompression()); + } + if(sndcpPacketHeader.getUDPHeaderCompression() != UDPHeaderCompression.NONE) + { + sb.append("UDP HEADER COMPRESSION:").append(sndcpPacketHeader.getUDPHeaderCompression()); + } + + sb.append(" ").append(getPacket()); + } + else + { + sb.append(" ").append(getPDUSequence().getHeader().toString()); + + if(getHeader().isConfirmationRequired()) + { + sb.append(" DATA BLOCKS ").append(getDataBlockSequenceNumbers()); + } + else + { + sb.append(" DATA BLOCKS:").append(getPDUSequence().getDataBlocks().size()); + } + + if(!getPDUSequence().getDataBlocks().isEmpty()) + { + sb.append(" MSG:"); + + for(DataBlock dataBlock : getPDUSequence().getDataBlocks()) + { + sb.append(dataBlock.getMessage().toHexString()); + } + } + + sb.append(" PAYLOAD:").append(getPayloadMessage().toHexString()); + sb.append(" ").append(getPacket()); + } + return sb.toString(); + } + + public List getDataBlockSequenceNumbers() + { + List numbers = new ArrayList<>(); + + for(DataBlock dataBlock: getPDUSequence().getDataBlocks()) + { + if(dataBlock instanceof ConfirmedDataBlock) + { + int sequenceNumber = ((ConfirmedDataBlock)dataBlock).getSequenceNumber(); + + if(sequenceNumber != -1) + { + numbers.add(sequenceNumber); + } + } + } + + return numbers; + } + + public IPacket getPacket() + { + if(mPacket == null) + { + mPacket = PacketMessageFactory.create(getSNDCPPacketHeader(), getPacketMessage(), 0); + } + + return mPacket; + } + + public BinaryMessage getPacketMessage() + { + if(mPacketMessage == null) + { + int start = getHeader().getDataHeaderOffset() * 8; + + if(getPayloadMessage().size() >= start + 32) + { + int end = getPayloadMessage().size() - 32; //CRC + end -= (getHeader().getPadOctetCount() * 8); + if(end > start) + { + mPacketMessage = getPayloadMessage().getSubMessage(start, end); + } + } + + if(mPacketMessage == null) + { + mPacketMessage = new BinaryMessage(0); + } + } + + return mPacketMessage; + } + + /** + * Combined binary payload of the data blocks including any header offset data and the 32-bit packet CRC. + * Note: for confirmed packets, this payload does not include the data block sequence number and block CRC data. + * Data blocks are assumed to be received in the correct order and no attempt is made to fill in for + * any missing data blocks. + */ + public BinaryMessage getPayloadMessage() + { + if(mPayload == null) + { + int octetCount = 0; + + if(getHeader().isConfirmationRequired()) + { + //Confirmed 3/4 rate encoded data blocks + octetCount += (16 * getHeader().getBlocksToFollowCount()); + } + else + { + //Unconfirmed 1/2 rate encoded data blocks + octetCount += (12 * getHeader().getBlocksToFollowCount()); + } + + mPayload = new BinaryMessage(octetCount * 8); + + int pointer = 0; + + List dataBlocks = getPDUSequence().getDataBlocks(); + + for(DataBlock dataBlock : dataBlocks) + { + BinaryMessage blockPayload = dataBlock.getMessage(); + mPayload.load(pointer, blockPayload); + pointer += blockPayload.size(); + } + } + + return mPayload; + } + + @Override + public DataUnitID getDUID() + { + return DataUnitID.IP_PACKET_DATA; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextAccept.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextAccept.java new file mode 100644 index 00000000..b69eadb4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextAccept.java @@ -0,0 +1,232 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.ipv4.APCO25IpAddress; +import io.github.dsheirer.module.decode.p25.reference.MDPConfigurationOption; +import io.github.dsheirer.module.decode.p25.reference.MaximumTransmitUnit; +import io.github.dsheirer.module.decode.p25.reference.NetworkAddressType; +import io.github.dsheirer.module.decode.p25.reference.PDUPriorityMaximumType; +import io.github.dsheirer.module.decode.p25.reference.ReadyTimer; +import io.github.dsheirer.module.decode.p25.reference.StandbyTimer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Activate Trunking Data Service (TDS) Context Request + */ +public class ActivateTdsContextAccept extends SNDCPMessage +{ + private static final int[] NSAPI = {4, 5, 6, 7}; + private static final int[] PDUPM = {8, 9, 10, 11}; + private static final int[] READY_TIMER = {12, 13, 14, 15}; + private static final int[] STANDBY_TIMER = {16, 17, 18, 19}; + private static final int[] NAT = {20, 21, 22, 23}; + private static final int[] IPV4_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int TCP_IP_HEADER_COMPRESSION = 56; + private static final int[] IP_HEADER_COMPRESSION_BITMAP = {56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] TCPSS = {64, 65, 66, 67}; + private static final int[] UDPSS = {68, 69, 70, 71}; + private static final int[] MTU = {72, 73, 74, 75}; + private static final int[] UDP_IP_HEADER_COMPRESSION_BITMAP = {76, 77, 78, 79}; + private static final int[] MDPCO = {80, 81, 82, 83, 84, 85, 86, 87}; + private static final int[] DATA_ACCESS_CONTROL = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + + private Identifier mIPAddress; + private List mIdentifiers; + + /** + * Constructs an SNDCP message parser instance. + * + * @param message containing the binary sequence + * @param outbound where true is outbound (from repeater) and false is inbound (from mobile) + */ + public ActivateTdsContextAccept(BinaryMessage message, boolean outbound) + { + super(message, outbound); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + sb.append(" NSAPI:").append(getNSAPI()); + if(hasIPAddress()) + { + sb.append(" ADDRESS:").append(getIPAddress()); + sb.append(" TYPE:").append(getNetworkAddressType()); + } + sb.append(" READY TIMER:").append(getReadyTimer().getSeconds()).append("s"); + sb.append(" STANDBY TIMER:").append(getStandbyTimer().getSeconds()).append("s"); + sb.append(" MTU:").append(getMaximumTransmitUnit().getByteCount()).append("B"); + sb.append(" AUTHORIZED GROUPS:").append(getDataAccessGroups()); + return sb.toString(); + } + + /** + * Network Service Access Point Identifier + */ + public int getNSAPI() + { + return getMessage().getInt(NSAPI); + } + + /** + * Maximum priority that can be used with PDUs for this context, to support differentiated + * quality of service. + */ + public PDUPriorityMaximumType getPDUMaximumPriority() + { + return PDUPriorityMaximumType.fromValue(getMessage().getInt(PDUPM)); + } + + public ReadyTimer getReadyTimer() + { + return ReadyTimer.fromValue(getMessage().getInt(READY_TIMER)); + } + + public StandbyTimer getStandbyTimer() + { + return StandbyTimer.fromValue(getMessage().getInt(STANDBY_TIMER)); + } + + public MaximumTransmitUnit getMaximumTransmitUnit() + { + return MaximumTransmitUnit.fromValue(getMessage().getInt(MTU)); + } + + /** + * Network Address Type + */ + public NetworkAddressType getNetworkAddressType() + { + return NetworkAddressType.fromValue(getMessage().getInt(NAT)); + } + + public boolean hasIPAddress() + { + NetworkAddressType nat = getNetworkAddressType(); + + return nat == NetworkAddressType.IPV4_STATIC_ADDRESS || + nat == NetworkAddressType.IPV4_DYNAMIC_ADDRESS; + } + + public Identifier getIPAddress() + { + if(mIPAddress == null && hasIPAddress()) + { + if(isOutbound()) + { + mIPAddress = APCO25IpAddress.createTo(getMessage().getInt(IPV4_ADDRESS)); + } + else + { + mIPAddress = APCO25IpAddress.createFrom(getMessage().getInt(IPV4_ADDRESS)); + } + } + + return mIPAddress; + } + + /** + * Data Access Control Groups. Indicates which (predefined) groups have access to the SNDCP context. + */ + public List getDataAccessGroups() + { + List groups = new ArrayList<>(); + for(int index : DATA_ACCESS_CONTROL) + { + if(getMessage().get(index)) + { + groups.add(index - 87); + } + } + + return groups; + } + /** + * Indicates if TCP/IP header compression is either being requested or is in use + */ + public boolean isTCPIPHeaderCompression() + { + return getMessage().get(TCP_IP_HEADER_COMPRESSION); + } + + /** + * Indicates if any form of IP header compression is being used/requested. + */ + public boolean isIPHeaderCompression() + { + return getMessage().getInt(IP_HEADER_COMPRESSION_BITMAP) > 0; + } + + /** + * Number of concurrent TCP/IP compressed connections when TCP/IP header compression is being used. See RFC 1144 + */ + public int getTCPIPStateSlots() + { + return getMessage().getInt(TCPSS); + } + + /** + * Number of concurrent UDP/IP compressed connnections when UDP/IP header compression is being used. + */ + public int getUDPIPStateSlots() + { + return getMessage().getInt(UDPSS); + } + + /** + * Indicates if any form of UDP/IP header compression is being used/requested. + */ + public boolean isUDPHeaderCompression() + { + return getMessage().getInt(UDP_IP_HEADER_COMPRESSION_BITMAP) > 0; + } + + /** + * MDP Configuration Option; + */ + public MDPConfigurationOption getMDPConfigurationOptions() + { + return MDPConfigurationOption.fromValue(getMessage().getInt(MDPCO)); + } + + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(hasIPAddress()) + { + mIdentifiers.add(getIPAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextReject.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextReject.java new file mode 100644 index 00000000..7afedc6a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextReject.java @@ -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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.reference.RejectReason; + +import java.util.Collections; +import java.util.List; + +/** + * Activate Trunking Data Service (TDS) Context Reject + */ +public class ActivateTdsContextReject extends SNDCPMessage +{ + private static final int[] NSAPI = {4, 5, 6, 7}; + private static final int[] REJECT_REASON = {8, 9, 10, 11, 12, 13, 14, 15}; + + /** + * Constructs an SNDCP message parser instance. + * + * @param message containing the binary sequence + * @param outbound where true is outbound (from repeater) and false is inbound (from mobile) + */ + public ActivateTdsContextReject(BinaryMessage message, boolean outbound) + { + super(message, outbound); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + sb.append(" NSAPI:").append(getNSAPI()); + sb.append(" REASON:").append(getRejectReason()); + return sb.toString(); + } + + /** + * Network Service Access Point Identifier + */ + public int getNSAPI() + { + return getMessage().getInt(NSAPI); + } + + + public RejectReason getRejectReason() + { + return RejectReason.fromValue(getMessage().getInt(REJECT_REASON)); + } + + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextRequest.java new file mode 100644 index 00000000..19dd5bdd --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/ActivateTdsContextRequest.java @@ -0,0 +1,200 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.ipv4.APCO25IpAddress; +import io.github.dsheirer.module.decode.p25.reference.DataSubscriberUnitType; +import io.github.dsheirer.module.decode.p25.reference.MDPConfigurationOption; +import io.github.dsheirer.module.decode.p25.reference.NetworkAddressType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Activate Trunking Data Service (TDS) Context Request + */ +public class ActivateTdsContextRequest extends SNDCPMessage +{ + private static final int[] VERSION = {4, 5, 6, 7}; + private static final int[] NSAPI = {8, 9, 10, 11}; + private static final int[] NAT = {12, 13, 14, 15}; + private static final int[] IPV4_ADDRESS = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] DSUT = {48, 49, 50, 51}; + private static final int[] UDP_IP_HEADER_COMPRESSION_BITMAP = {52, 53, 54, 55}; + private static final int[] IP_HEADER_COMPRESSION_BITMAP = {56, 57, 58, 59, 60, 61, 62, 63}; + private static final int TCP_IP_HEADER_COMPRESSION = 56; + private static final int[] TCPSS = {64, 65, 66, 67}; + private static final int[] UDPSS = {68, 69, 70, 71}; + private static final int[] MDPCO = {72, 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mIPAddress; + private List mIdentifiers; + + /** + * Constructs an SNDCP message parser instance. + * + * @param message containing the binary sequence + * @param outbound where true is outbound (from repeater) and false is inbound (from mobile) + */ + public ActivateTdsContextRequest(BinaryMessage message, boolean outbound) + { + super(message, outbound); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getPDUType()); + if(hasIPAddress()) + { + sb.append(" IP:").append(getIPAddress()); + sb.append(" TYPE:").append(getNetworkAddressType()); + } + + sb.append(" NSAPI:").append(getNSAPI()); + + return sb.toString(); + } + + /** + * SNDCP Version Number + */ + public int getVersion() + { + return getMessage().getInt(VERSION); + } + + /** + * Network Service Access Point Identifier + */ + public int getNSAPI() + { + return getMessage().getInt(NSAPI); + } + + /** + * Network Address Type + */ + public NetworkAddressType getNetworkAddressType() + { + return NetworkAddressType.fromValue(getMessage().getInt(NAT)); + } + + public boolean hasIPAddress() + { + NetworkAddressType nat = getNetworkAddressType(); + + return nat == NetworkAddressType.IPV4_STATIC_ADDRESS || + nat == NetworkAddressType.IPV4_DYNAMIC_ADDRESS; + } + + public Identifier getIPAddress() + { + if(mIPAddress == null && hasIPAddress()) + { + if(isOutbound()) + { + mIPAddress = APCO25IpAddress.createTo(getMessage().getInt(IPV4_ADDRESS)); + } + else + { + mIPAddress = APCO25IpAddress.createFrom(getMessage().getInt(IPV4_ADDRESS)); + } + } + + return mIPAddress; + } + + /** + * Identifies the type of user equipment + */ + public DataSubscriberUnitType getDataSubsciberUnitType() + { + return DataSubscriberUnitType.fromValue(getMessage().getInt(DSUT)); + } + + /** + * Indicates if TCP/IP header compression is either being requested or is in use + */ + public boolean isTCPIPHeaderCompression() + { + return getMessage().get(TCP_IP_HEADER_COMPRESSION); + } + + /** + * Indicates if any form of IP header compression is being used/requested. + */ + public boolean isIPHeaderCompression() + { + return getMessage().getInt(IP_HEADER_COMPRESSION_BITMAP) > 0; + } + + /** + * Number of concurrent TCP/IP compressed connections when TCP/IP header compression is being used. See RFC 1144 + */ + public int getTCPIPStateSlots() + { + return getMessage().getInt(TCPSS); + } + + /** + * Number of concurrent UDP/IP compressed connnections when UDP/IP header compression is being used. + */ + public int getUDPIPStateSlots() + { + return getMessage().getInt(UDPSS); + } + + /** + * Indicates if any form of UDP/IP header compression is being used/requested. + */ + public boolean isUDPHeaderCompression() + { + return getMessage().getInt(UDP_IP_HEADER_COMPRESSION_BITMAP) > 0; + } + + /** + * MDP Configuration Option; + */ + public MDPConfigurationOption getMDPConfigurationOptions() + { + return MDPConfigurationOption.fromValue(getMessage().getInt(MDPCO)); + } + + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(hasIPAddress()) + { + mIdentifiers.add(getIPAddress()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/DeActivateTdsContextRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/DeActivateTdsContextRequest.java new file mode 100644 index 00000000..2d86fc38 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/DeActivateTdsContextRequest.java @@ -0,0 +1,98 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * De-Activate Trunking Data Service (TDS) Context Request + */ +public class DeActivateTdsContextRequest extends SNDCPMessage +{ + private static final int[] NSAPI = {4, 5, 6, 7}; + private static final int[] DEACTIVATION_TYPE = {8, 9, 10, 11, 12, 13, 14, 15}; + + /** + * Constructs an SNDCP message parser instance. + * + * @param message containing the binary sequence + * @param outbound where true is outbound (from repeater) and false is inbound (from mobile) + */ + public DeActivateTdsContextRequest(BinaryMessage message, boolean outbound) + { + super(message, outbound); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + sb.append(" DEACTIVATE NSAPIs ").append(getDeactivateNSAPIs()); + return sb.toString(); + } + + /** + * Network Service Access Point Identifier + */ + public int getNSAPI() + { + return getMessage().getInt(NSAPI); + } + + /** + * List of NSAPIs to deactivate for the mobile subscriber + */ + public List getDeactivateNSAPIs() + { + int type = getMessage().getInt(DEACTIVATION_TYPE); + + if(type == 0) + { + List allNSAPIs = new ArrayList<>(); + + for(int x = 0; x < 16; x++) + { + allNSAPIs.add(x); + } + + return allNSAPIs; + } + else if(type == 1) + { + List thisNSAPI = new ArrayList<>(); + thisNSAPI.add(getNSAPI()); + return thisNSAPI; + } + + return Collections.EMPTY_LIST; + } + + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessage.java new file mode 100644 index 00000000..adfef6e8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessage.java @@ -0,0 +1,92 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.p25.reference.PDUType; + +/** + * Subnetwork Dependent Convergence Protocol (SNDCP) base message parser + */ +public class SNDCPMessage +{ + private static final int[] PDU_TYPE = {0, 1, 2, 3}; + + private BinaryMessage mMessage; + private boolean mOutbound; + + /** + * Constructs an SNDCP message parser instance. + * + * @param message containing the binary sequence + * @param outbound where true is outbound (from repeater) and false is inbound (from mobile) + */ + public SNDCPMessage(BinaryMessage message, boolean outbound) + { + mMessage = message; + mOutbound = outbound; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getPDUType()); + return sb.toString(); + } + + /** + * Binary message sequence + */ + public BinaryMessage getMessage() + { + return mMessage; + } + + /** + * Indicates if this is an outbound (versus inbound) SNDCP message + */ + public boolean isOutbound() + { + return mOutbound; + } + + /** + * Indicates the type of SNDCP packet contained in this message + */ + public PDUType getPDUType() + { + return getPDUType(getMessage(), isOutbound()); + } + + /** + * Determines PDU type of the message + * + * @param message containing a PDU type field + * @param outbound where true is outbound and false is inbound + * @return pdu type + */ + public static PDUType getPDUType(BinaryMessage message, boolean outbound) + { + return PDUType.fromValue(message.getInt(PDU_TYPE), outbound); + } + +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessageFactory.java new file mode 100644 index 00000000..ed4c633e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPMessageFactory.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.p25.reference.PDUType; + +public class SNDCPMessageFactory +{ + public static SNDCPMessage create(BinaryMessage binaryMessage, boolean outbound) + { + PDUType pduType = SNDCPMessage.getPDUType(binaryMessage, outbound); + + switch(pduType) + { + case OUTBOUND_SNDCP_RF_UNCONFIRMED_DATA: + //Note: (un)confirmed data type is processed by the PDUMessageFactory as a packet message + case OUTBOUND_SNDCP_RF_CONFIRMED_DATA: + //Note: (un)confirmed data type is processed by the PDUMessageFactory as a packet message + case INBOUND_SNDCP_RF_CONFIRMED_DATA: + //Note: (un)confirmed data type is processed by the PDUMessageFactory as a packet message + case OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: + return new ActivateTdsContextAccept(binaryMessage, outbound); + case INBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: + return new ActivateTdsContextRequest(binaryMessage, outbound); + case OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: + return new ActivateTdsContextReject(binaryMessage, outbound); + case OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: + return new DeActivateTdsContextRequest(binaryMessage, outbound); + case OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: + case INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: + case INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: + + case OUTBOUND_UNKNOWN: + case INBOUND_UNKNOWN: + default: + return new SNDCPMessage(binaryMessage, outbound); + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketHeader.java new file mode 100644 index 00000000..f191f0f3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketHeader.java @@ -0,0 +1,120 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.ip.Header; +import io.github.dsheirer.module.decode.p25.reference.IPHeaderCompression; +import io.github.dsheirer.module.decode.p25.reference.PDUType; +import io.github.dsheirer.module.decode.p25.reference.UDPHeaderCompression; + +/** + * SNDCP header for a packet message. This header contains IP and UDP compression indicators. + * + * Note: this header can be constructed with no data to ensure that it is never null for the case + * of packet messages that do not contain an SNDCP header. + */ +public class SNDCPPacketHeader extends Header +{ + private static final int[] PDU_TYPE = {0, 1, 2, 3}; + private static final int[] NSAPI = {4, 5, 6, 7}; + private static final int[] PACKET_HEADER_COMPRESSION = {8, 9, 10, 11}; + private static final int[] DATAGRAM_HEADER_COMPRESSION = {12, 13, 14, 15}; + + private boolean mOutboundMessage; + + public SNDCPPacketHeader(BinaryMessage message, boolean outboundMessage) + { + super(message, 0); + mOutboundMessage = outboundMessage; + } + + @Override + public int getLength() + { + return getMessage().size(); + } + + /** + * Constructs an empty header with no data. + */ + public SNDCPPacketHeader(boolean outboundMessage) + { + this(new BinaryMessage(0), outboundMessage); + } + + public boolean isOutboundMessage() + { + return mOutboundMessage; + } + + /** + * Indicates if this header has data or if it is an empty header + */ + public boolean hasData() + { + return getLength() == 16; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(hasData()) + { + sb.append(getPDUType()); + sb.append(" NSAPI:").append(getNSAPI()); + sb.append(" IP COMPRESSION:").append(getIPHeaderCompression()); + sb.append(" UDP COMPRESSION:").append(getUDPHeaderCompression()); + } + else + { + sb.append("NO SNDCP HEADER"); + } + + return sb.toString(); + } + + public PDUType getPDUType() + { + if(hasData()) + { + return PDUType.fromValue(getMessage().getInt(PDU_TYPE), isOutboundMessage()); + } + + return isOutboundMessage() ? PDUType.OUTBOUND_UNKNOWN : PDUType.INBOUND_UNKNOWN; + } + + public int getNSAPI() + { + return getMessage().getInt(NSAPI); + } + + public IPHeaderCompression getIPHeaderCompression() + { + return IPHeaderCompression.fromValue(getMessage().getInt(PACKET_HEADER_COMPRESSION)); + } + + public UDPHeaderCompression getUDPHeaderCompression() + { + return UDPHeaderCompression.fromValue(getMessage().getInt(DATAGRAM_HEADER_COMPRESSION)); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketMessage.java new file mode 100644 index 00000000..aba6e2f1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/packet/sndcp/SNDCPPacketMessage.java @@ -0,0 +1,70 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp; + +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.packet.PacketMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +public class SNDCPPacketMessage extends PacketMessage +{ + private SNDCPMessage mSNDCPMessage; + + public SNDCPPacketMessage(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + + sb.append(" LLID:").append(getHeader().getLLID()); + if(getPDUSequence().isComplete()) + { + sb.append(" ").append(getSNDCPMessage().toString()); + sb.append(" MSG:").append(getPacketMessage().toHexString()); + } + else + { + sb.append(" ***INCOMPLETE DATA BLOCKS RECEIVED***"); + } + + return sb.toString(); + } + + @Override + public DataUnitID getDUID() + { + return DataUnitID.SUBNETWORK_DEPENDENT_CONVERGENCE_PROTOCOL; + } + + public SNDCPMessage getSNDCPMessage() + { + if(mSNDCPMessage == null) + { + mSNDCPMessage = SNDCPMessageFactory.create(getPacketMessage(), getPDUSequence().getHeader().isOutbound()); + } + + return mSNDCPMessage; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseHeader.java new file mode 100644 index 00000000..4958ec9e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseHeader.java @@ -0,0 +1,92 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.pdu.response; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUHeader; +import io.github.dsheirer.module.decode.p25.reference.PacketResponse; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +public class ResponseHeader extends PDUHeader +{ + public static final int[] RESPONSE = {8,9,10,11,12,13,14,15}; + public static final int SOURCE_LLID_FLAG = 48; + public static final int[] FROM_LOGICAL_LINK_ID = {56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79}; + + public ResponseHeader(CorrectedBinaryMessage message, boolean passesCRC) + { + super(message, passesCRC); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("***CRC-FAIL*** "); + } + + sb.append("PDU RESPONSE"); + + if(hasSourceLLID()) + { + sb.append(" FROM:").append(getFromLogicalLinkID()); + } + + sb.append(" TO:").append(getLLID()); + + sb.append(" ").append(getResponse()); + + Vendor vendor = getVendor(); + + if(vendor != Vendor.STANDARD) + { + sb.append(" VENDOR:").append(getVendor()); + } + + return sb.toString(); + } + + /** + * Packet response message + */ + public PacketResponse getResponse() + { + return PacketResponse.fromValue(getMessage().getInt(RESPONSE)); + } + + /** + * Indicates if this header contains a source (from) LLID + */ + public boolean hasSourceLLID() + { + return !getMessage().get(SOURCE_LLID_FLAG); + } + + /** + * Source Logical Link Identifier (ie FROM radio identifier) + */ + public String getFromLogicalLinkID() + { + return getMessage().getHex(FROM_LOGICAL_LINK_ID, 6); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseMessage.java new file mode 100644 index 00000000..41fd3465 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/response/ResponseMessage.java @@ -0,0 +1,157 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.response; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequenceMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.block.DataBlock; +import io.github.dsheirer.module.decode.p25.reference.PacketResponse; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ResponseMessage extends PDUSequenceMessage +{ + public ResponseMessage(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + private ResponseHeader getHeader() + { + return (ResponseHeader)getPDUSequence().getHeader(); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("NAC:").append(getNAC()); + + sb.append(" ").append(getHeader().toString()); + + if(getPDUSequence().isComplete()) + { + if(getHeader().getResponse() == PacketResponse.SELECTIVE_RETRY) + { + sb.append(" - RESEND BLOCKS ").append(getMissingBlockNumbers()); + } + else + { + if(getPDUSequence().getDataBlocks().size() > 0) + { + sb.append(" BLOCKS TO FOLLOW:").append(getPDUSequence().getDataBlocks().size()); + + sb.append(" DATA BLOCKS:").append(getPDUSequence().getDataBlocks().size()); + + if(!getPDUSequence().getDataBlocks().isEmpty()) + { + sb.append(" MSG:"); + + for(DataBlock dataBlock: getPDUSequence().getDataBlocks()) + { + sb.append(dataBlock.getMessage().toHexString()); + } + } + } + } + } + else + { + sb.append(" *INCOMPLETE - RECEIVED ").append(getPDUSequence().getDataBlocks().size()).append("/") + .append(getHeader().getBlocksToFollowCount()).append(" DATA BLOCKS"); + } + + + return sb.toString(); + } + + /** + * Identifies missing block numbers for Selective Retry type response messages. + */ + public List getMissingBlockNumbers() + { + int dataBlockCount = getPDUSequence().getDataBlocks().size(); + + if(0 < dataBlockCount && dataBlockCount <= 2) + { + List missingBlockNumbers = new ArrayList<>(); + + int blockOffset = 0; + + for(DataBlock dataBlock: getPDUSequence().getDataBlocks()) + { + missingBlockNumbers.addAll(getMissingBlockNumbers(dataBlock, blockOffset)) ; + + blockOffset += 64; + } + + Collections.sort(missingBlockNumbers); + return missingBlockNumbers; + } + + return Collections.EMPTY_LIST; + } + + /** + * Identifies the missing block numbers for a selective retry type response message from the + * appended data block. Up to 127 (max block size for a PDU) missing blocks can be identified + * across 2 data blocks with each data block holding a maximum of 64 block indices. + * + * The format for the missing block indices is arranged in a super easy (not) to parse format: + * + * 7 6 5 4 3 2 1 0 + * 15 14 13 12 ... + * ... + * 63 62 61 60 59 58 57 54 + * + * @param dataBlock + * @param offset + * @return + */ + private List getMissingBlockNumbers(DataBlock dataBlock, int offset) + { + List missingBlockNumbers = new ArrayList<>(); + + int blockIndex = 7; + + BinaryMessage message = dataBlock.getMessage(); + + for(int index = 0; index < 64; index++) + { + if(!message.get(index)) + { + missingBlockNumbers.add(blockIndex + offset); + } + + blockIndex--; + + if((blockIndex ^ 8) == 0) + { + blockIndex += 8; + } + } + + return missingBlockNumbers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCHeader.java new file mode 100644 index 00000000..57f237bf --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCHeader.java @@ -0,0 +1,92 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.pdu.umbtc; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.P25Utils; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUHeader; +import io.github.dsheirer.module.decode.p25.reference.ServiceAccessPoint; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +/** + * Unconfirmed Multi-Block Trunking Control Header + */ +public class UMBTCHeader extends PDUHeader +{ + public static final int[] SAP_ID = {10, 11, 12, 13, 14, 15}; + public static final int[] PAD_OCTET_COUNT = {59, 60, 61, 62, 63}; + public static final int[] RESERVED = {64, 65, 66, 67, 68, 69, 70, 71}; + public static final int[] DATA_HEADER_OFFSET = {74, 75, 76, 77, 78, 79}; + + public UMBTCHeader(CorrectedBinaryMessage message, boolean passesCRC) + { + super(message, passesCRC); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("*CRC-FAIL*"); + } + + sb.append("UMBTC"); + + Vendor vendor = getVendor(); + + P25Utils.pad(sb, 22); + + if(vendor != Vendor.STANDARD) + { + sb.append(" VENDOR:").append(getVendor()); + } + + sb.append(" HDR:").append(getMessage().toHexString()); + return sb.toString(); + } + + /** + * Service Access Point (SAP) - determines the network service that will process this packet + */ + public ServiceAccessPoint getServiceAccessPoint() + { + return ServiceAccessPoint.fromValue(getMessage().getInt(SAP_ID)); + } + + /** + * Number of octets (bytes) that are appended to the end of the packet to make a full final block + */ + public int getPadOctetCount() + { + return getMessage().getInt(PAD_OCTET_COUNT); + } + + /** + * Number of bits to where the header + * @return + */ + public int getDataHeaderOffset() + { + return getMessage().getInt(DATA_HEADER_OFFSET); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCMessage.java new file mode 100644 index 00000000..534e0c62 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/UMBTCMessage.java @@ -0,0 +1,149 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.umbtc; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.IBitErrorProvider; +import io.github.dsheirer.module.decode.p25.P25Utils; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.block.DataBlock; +import io.github.dsheirer.module.decode.p25.message.pdu.block.UnconfirmedDataBlock; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.List; + +public abstract class UMBTCMessage extends P25Message implements IBitErrorProvider +{ + protected static final int[] HEADER_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BLOCK_0_OPCODE = {2, 3, 4, 5, 6, 7}; + + private PDUSequence mPDUSequence; + + public UMBTCMessage(PDUSequence PDUSequence, int nac, long timestamp) + { + super(nac, timestamp); + mPDUSequence = PDUSequence; + } + + @Override + protected String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.getMessageStub()); + sb.append(" ").append(getOpcode()); + P25Utils.pad(sb, 30); + + return sb.toString(); + } + + public UMBTCHeader getHeader() + { + return (UMBTCHeader)getPDUSequence().getHeader(); + } + + public Opcode getOpcode() + { + if(hasDataBlock(0)) + { + return Opcode.fromValue(getDataBlock(0).getMessage().getInt(BLOCK_0_OPCODE), + getHeader().getDirection(), getHeader().getVendor()); + } + + return Opcode.OSP_UNKNOWN; + } + + public boolean hasDataBlock(int index) + { + return getDataBlock(index) != null; + } + + public UnconfirmedDataBlock getDataBlock(int index) + { + DataBlock dataBlock = getPDUSequence().getDataBlocks().get(index); + + if(dataBlock instanceof UnconfirmedDataBlock) + { + return (UnconfirmedDataBlock)dataBlock; + } + + return null; + } + + + @Override + public DataUnitID getDUID() + { + return DataUnitID.UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL; + } + + /** + * List of identifiers provided by the message + */ + public abstract List getIdentifiers(); + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NAC:").append(getNAC()); + + if(!getPDUSequence().isComplete()) + { + sb.append(" *INCOMPLETE - RECEIVED ").append(getPDUSequence().getDataBlocks().size()).append("/") + .append(getPDUSequence().getHeader().getBlocksToFollowCount()).append(" DATA BLOCKS"); + } + + sb.append(" ").append(getPDUSequence().getHeader().toString()); + + sb.append(" DATA BLOCKS:").append(getPDUSequence().getDataBlocks().size()); + + if(!getPDUSequence().getDataBlocks().isEmpty()) + { + sb.append(" MSG:"); + + for(DataBlock dataBlock : getPDUSequence().getDataBlocks()) + { + sb.append(dataBlock.getMessage().toHexString()); + } + } + + return sb.toString(); + } + + public PDUSequence getPDUSequence() + { + return mPDUSequence; + } + + @Override + public int getBitsProcessedCount() + { + return getPDUSequence().getBitsProcessedCount(); + } + + @Override + public int getBitErrorsCount() + { + return getPDUSequence().getBitErrorsCount(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/isp/UMBTCTelephoneInterconnectRequestExplicitDialing.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/isp/UMBTCTelephoneInterconnectRequestExplicitDialing.java new file mode 100644 index 00000000..9cc67e26 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/pdu/umbtc/isp/UMBTCTelephoneInterconnectRequestExplicitDialing.java @@ -0,0 +1,151 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.pdu.umbtc.isp; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.message.IBitErrorProvider; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.telephone.APCO25TelephoneNumber; +import io.github.dsheirer.module.decode.p25.message.pdu.PDUSequence; +import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.UMBTCMessage; +import io.github.dsheirer.module.decode.p25.reference.Digit; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class UMBTCTelephoneInterconnectRequestExplicitDialing extends UMBTCMessage implements IBitErrorProvider +{ + private static final int[] BLOCK_0_DIGIT_COUNT = {8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] BLOCK_0_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mSourceAddress; + private Identifier mTelephoneNumber; + private List mIdentifiers; + + public UMBTCTelephoneInterconnectRequestExplicitDialing(PDUSequence PDUSequence, int nac, long timestamp) + { + super(PDUSequence, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(getTelephoneNumber() != null) + { + sb.append(" TO:").append(getTelephoneNumber()); + } + if(getVoiceServiceOptions() != null) + { + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + } + + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null && hasDataBlock(0)) + { + ; + } + { + mVoiceServiceOptions = new VoiceServiceOptions(getDataBlock(0).getMessage().getInt(BLOCK_0_SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getHeader().getMessage().getInt(HEADER_ADDRESS)); + } + + return mSourceAddress; + } + + public Identifier getTelephoneNumber() + { + if(mTelephoneNumber == null && hasDataBlock(0)) + { + int digitCount = getDataBlock(0).getMessage().getInt(BLOCK_0_DIGIT_COUNT); + List digits = new ArrayList<>(); + + for(int x = 1; x <= digitCount; x++) + { + digits.add(getDigit(x)); + } + + mTelephoneNumber = APCO25TelephoneNumber.createTo(Digit.decode(digits)); + } + + return mTelephoneNumber; + } + + /** + * Returns the specified digit. + * + * @param digit in range 1 - 34 + * @return specified digit or Digit.UNKNOWN + */ + private int getDigit(int digit) + { + if(digit <= 18) //Block 0 + { + if(hasDataBlock(0)) + { + int startIndex = 24 + ((digit - 1) * 4); + return getDataBlock(0).getMessage().getInt(startIndex, startIndex + 3); + } + } + else if(digit <= 34) //Block 1 + { + if(hasDataBlock(1)) + { + int startIndex = 0 + ((digit - 19) * 4); + return getDataBlock(0).getMessage().getInt(startIndex, startIndex + 3); + } + } + + return -1; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + if(getTelephoneNumber() != null) + { + mIdentifiers.add(getTelephoneNumber()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDULinkControlMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDULinkControlMessage.java new file mode 100644 index 00000000..3361da52 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDULinkControlMessage.java @@ -0,0 +1,199 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.tdu; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.edac.Golay24; +import io.github.dsheirer.edac.ReedSolomon_63_47_17; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.P25Message; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWordFactory; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +public class TDULinkControlMessage extends P25Message implements IFrequencyBandReceiver +{ + public static final int[] LC_HEX_0 = {0, 1, 2, 3, 4, 5}; + public static final int[] LC_HEX_1 = {6, 7, 8, 9, 10, 11}; + public static final int[] LC_HEX_2 = {24, 25, 26, 27, 28, 29}; + public static final int[] LC_HEX_3 = {30, 31, 32, 33, 34, 35}; + public static final int[] LC_HEX_4 = {48, 49, 50, 51, 52, 53}; + public static final int[] LC_HEX_5 = {54, 55, 56, 57, 58, 59}; + public static final int[] LC_HEX_6 = {72, 73, 74, 75, 76, 77}; + public static final int[] LC_HEX_7 = {78, 79, 80, 81, 82, 83}; + public static final int[] LC_HEX_8 = {96, 97, 98, 99, 100, 101}; + public static final int[] LC_HEX_9 = {102, 103, 104, 105, 106, 107}; + public static final int[] LC_HEX_10 = {120, 121, 122, 123, 124, 125}; + public static final int[] LC_HEX_11 = {126, 127, 128, 129, 130, 131}; + public static final int[] RS_HEX_0 = {144, 145, 146, 147, 148, 149}; + public static final int[] RS_HEX_1 = {150, 151, 152, 153, 154, 155}; + public static final int[] RS_HEX_2 = {168, 169, 170, 171, 172, 173}; + public static final int[] RS_HEX_3 = {174, 175, 176, 177, 178, 179}; + public static final int[] RS_HEX_4 = {192, 193, 194, 195, 196, 197}; + public static final int[] RS_HEX_5 = {198, 199, 200, 201, 202, 203}; + public static final int[] RS_HEX_6 = {216, 217, 218, 219, 220, 221}; + public static final int[] RS_HEX_7 = {222, 223, 224, 225, 226, 227}; + public static final int[] RS_HEX_8 = {240, 241, 242, 243, 244, 245}; + public static final int[] RS_HEX_9 = {246, 247, 248, 249, 250, 251}; + public static final int[] RS_HEX_10 = {264, 265, 266, 267, 268, 269}; + public static final int[] RS_HEX_11 = {270, 271, 272, 273, 274, 275}; + + private LinkControlWord mLinkControlWord; + + public TDULinkControlMessage(CorrectedBinaryMessage message, int nac, long timestamp) + { + super(message, nac, timestamp); + } + + @Override + public DataUnitID getDUID() + { + return DataUnitID.TERMINATOR_DATA_UNIT_LINK_CONTROL; + } + + public LinkControlWord getLinkControlWord() + { + if(mLinkControlWord == null) + { + createLinkControlWord(); + } + + return mLinkControlWord; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + sb.append(" ").append(getLinkControlWord()); + + return sb.toString(); + } + + /** + * Checks and repairs the 12 x 24-bit golay(24,12,8) codewords and then + * checks and repairs the 24 x 6-bit link control and reed-solomon + * RS(24,12,13) parity codewords + */ + private void createLinkControlWord() + { + //Check the Golay codewords + int codewordPointer = 0; + + while(codewordPointer < getMessage().size()) + { + int corrected = Golay24.checkAndCorrect(getMessage(), codewordPointer); + codewordPointer += 24; + } + + //Check the Reed-Solomon parity bits. The RS decoder expects the link control data and reed solomon parity hex + // codewords in reverse order. The RS(24,12,13) code used by P25 removes the left-hand 47 data hex words, so we + // replace them with zeros. */ + int[] input = new int[63]; + int[] output = new int[63]; + + input[0] = getMessage().getInt(RS_HEX_11); + input[1] = getMessage().getInt(RS_HEX_10); + input[2] = getMessage().getInt(RS_HEX_9); + input[3] = getMessage().getInt(RS_HEX_8); + input[4] = getMessage().getInt(RS_HEX_7); + input[5] = getMessage().getInt(RS_HEX_6); + input[6] = getMessage().getInt(RS_HEX_5); + input[7] = getMessage().getInt(RS_HEX_4); + input[8] = getMessage().getInt(RS_HEX_3); + input[9] = getMessage().getInt(RS_HEX_2); + input[10] = getMessage().getInt(RS_HEX_1); + input[11] = getMessage().getInt(RS_HEX_0); + input[12] = getMessage().getInt(LC_HEX_11); + input[13] = getMessage().getInt(LC_HEX_10); + input[14] = getMessage().getInt(LC_HEX_9); + input[15] = getMessage().getInt(LC_HEX_8); + input[16] = getMessage().getInt(LC_HEX_7); + input[17] = getMessage().getInt(LC_HEX_6); + input[18] = getMessage().getInt(LC_HEX_5); + input[19] = getMessage().getInt(LC_HEX_4); + input[20] = getMessage().getInt(LC_HEX_3); + input[21] = getMessage().getInt(LC_HEX_2); + input[22] = getMessage().getInt(LC_HEX_1); + input[23] = getMessage().getInt(LC_HEX_0); + /* indexes 24 - 62 are defaulted to zero */ + + //Reed-Solomon(24,12,13) code protects the link control word. Maximum correctable errors are: 6 + ReedSolomon_63_47_17 reedSolomon_63_47_17 = new ReedSolomon_63_47_17(6); + + boolean irrecoverableErrors = reedSolomon_63_47_17.decode(input, output); + + //Transfer error corrected output to a new binary message + BinaryMessage binaryMessage = new BinaryMessage(72); + + int pointer = 0; + + for(int x = 23; x >= 12; x--) + { + if(output[x] != -1) + { + binaryMessage.load(pointer, 6, output[x]); + } + + pointer += 6; + } + + mLinkControlWord = LinkControlWordFactory.create(binaryMessage); + + if(irrecoverableErrors) + { + mLinkControlWord.setValid(false); + } + + //If we corrected any bit errors, update the original message with the bit error count + for(int x = 0; x < 23; x++) + { + if(output[x] != input[x]) + { + getMessage().incrementCorrectedBitCount(Integer.bitCount((output[x] ^ input[x]))); + } + } + } + + @Override + public List getChannels() + { + if(getLinkControlWord().isValid() && getLinkControlWord() instanceof IFrequencyBandReceiver) + { + return ((IFrequencyBandReceiver)getLinkControlWord()).getChannels(); + } + + return Collections.EMPTY_LIST; + } + + @Override + public List getIdentifiers() + { + return getLinkControlWord().getIdentifiers(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDUMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDUMessage.java index 9bc63985..a0274c34 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDUMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/TDUMessage.java @@ -1,54 +1,52 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 + * sdr-trunk + * 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 + * ******************************************************************************/ package io.github.dsheirer.module.decode.p25.message.tdu; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.p25.message.P25Message; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import java.util.Collections; +import java.util.List; + public class TDUMessage extends P25Message { - public TDUMessage( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) + public TDUMessage(CorrectedBinaryMessage message, int nac, long timestamp) { - super( message, duid, aliasList ); - - /* NID CRC is checked in the message framer, thus a constructed message - * means it passed the CRC */ - mCRC = new CRC[ 1 ]; - mCRC[ 0 ] = CRC.PASSED; + super(message, nac, timestamp); } - @Override - public String getMessage() + @Override + public DataUnitID getDUID() { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - - sb.append( " TERMINATOR DATA UNIT" ); - - return sb.toString(); + return DataUnitID.TERMINATOR_DATA_UNIT; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + return sb.toString(); + } + + public List getIdentifiers() + { + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java deleted file mode 100644 index 45aee039..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java +++ /dev/null @@ -1,170 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class AdjacentSiteStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver, IAdjacentSite -{ - public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; - public static final int[] SYSTEM_ID = {96, 97, 98, 99, 112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; - public static final int[] IDENTIFIER = {160, 161, 162, 163}; - public static final int[] CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; - public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mIdentifierUpdate; - - public AdjacentSiteStatusBroadcast(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLRA()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); - - sb.append(" CHAN:" + getChannel()); - - sb.append(" " + getSystemServiceClass()); - - return sb.toString(); - } - - public String getUniqueID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getSystemID()); - sb.append(":"); - sb.append(getRFSS()); - sb.append(":"); - sb.append(getSiteID()); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex(LRA, 2); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSS() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - @Override - public String getDownlinkChannel() - { - return getChannel(); - } - - @Override - public String getUplinkChannel() - { - return getChannel(); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public String getSystemServiceClass() - { - return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java deleted file mode 100644 index 669d58d8..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class AdjacentSiteStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver, IAdjacentSite -{ - public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; - public static final int[] TRANSMIT_IDENTIFIER = {92, 93, 94, 95}; - public static final int[] TRANSMIT_CHANNEL = {96, 97, 98, 99, 112, 113, 114, 115, - 116, 117, 118, 119}; - public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; - - public static final int[] RECEIVE_IDENTIFIER = {160, 161, 162, 163}; - public static final int[] RECEIVE_CHANNEL = {164, 165, 166, 167, 168, 169, 170, - 171, 184, 185, 186, 187}; - - private IBandIdentifier mTransmitBand; - private IBandIdentifier mReceiveBand; - - public AdjacentSiteStatusBroadcastExplicit(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLRA()); - - sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); - - sb.append(" DNLINK:" + getDownlinkChannel()); - - sb.append(" UPLINK:" + getUplinkChannel()); - - return sb.toString(); - } - - public String getUniqueID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getSystemID()); - sb.append(":"); - sb.append(getRFSS()); - sb.append(":"); - sb.append(getSiteID()); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex(LRA, 2); - } - - public String getRFSS() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSystemID() - { - return "***"; - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - @Override - public String getSystemServiceClass() - { - return "[]"; - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannel() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getDownlinkChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannel(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannel() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getUplinkChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannel(); - } - - public long getDownlinkFrequency() - { - if(mTransmitBand != null) - { - return mTransmitBand.getDownlinkFrequency(getTransmitChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveBand != null) - { - return mReceiveBand.getUplinkFrequency(getReceiveChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitBand = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveBand = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallAlert.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallAlert.java deleted file mode 100644 index 60d288e6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallAlert.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class CallAlert extends TDULinkControlMessage -{ - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,190,192,193,194,195 }; - - public CallAlert( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CALL_ALERT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallTermination.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallTermination.java deleted file mode 100644 index 721af006..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/CallTermination.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class CallTermination extends TDULinkControlMessage -{ - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public CallTermination( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " BY:" + getSourceAddress() ); - - return sb.toString(); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java deleted file mode 100644 index 647838d5..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class ChannelIdentifierUpdate extends TDULinkControlMessage implements IBandIdentifier -{ - public static final int[] IDENTIFIER = {72, 73, 74, 75}; - public static final int[] BANDWIDTH = {88, 89, 90, 91, 92, 93, 94, 95, 96}; - public static final int[] TRANSMIT_OFFSET = {97, 98, 99, 112, 113, 114, 115, 116, - 117}; - public static final int[] CHANNEL_SPACING = {118, 119, 120, 121, 122, 123, 136, - 137, 138, 139}; - public static final int[] BASE_FREQUENCY = {140, 141, 142, 143, 144, 145, 146, - 147, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, - 187, 188, 189, 190, 191, 192, 193, 194, 195}; - - public ChannelIdentifierUpdate(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" IDEN:" + getIdentifier()); - - sb.append(" BASE:" + getBaseFrequency()); - - sb.append(" BW:" + getBandwidth()); - - sb.append(" SPACING:" + getChannelSpacing()); - - sb.append(" OFFSET:" + getTransmitOffset()); - - return sb.toString(); - } - - @Override - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - /** - * Channel bandwidth in hertz - */ - @Override - public int getBandwidth() - { - return mMessage.getInt(BANDWIDTH) * 125; - } - - @Override - public long getChannelSpacing() - { - return mMessage.getLong(CHANNEL_SPACING) * 125l; - } - - @Override - public long getBaseFrequency() - { - return mMessage.getLong(BASE_FREQUENCY) * 5l; - } - - @Override - public long getTransmitOffset() - { - return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l; - } - - @Override - public long getDownlinkFrequency(int channelNumber) - { - return getBaseFrequency() + (channelNumber * getChannelSpacing()); - } - - @Override - public long getUplinkFrequency(int channelNumber) - { - return getDownlinkFrequency(channelNumber) + getTransmitOffset(); - } - - @Override - public boolean isTDMA() - { - return false; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java deleted file mode 100644 index 7858ec13..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class ChannelIdentifierUpdateExplicit extends TDULinkControlMessage implements IBandIdentifier -{ - public static final int[] IDENTIFIER = {72, 73, 74, 75}; - public static final int[] BANDWIDTH = {88, 89, 90, 91}; - public static final int[] TRANSMIT_OFFSET = {92, 93, 94, 95, 96, 97, 98, 99, 112, 113, 114, 115, 116, 117}; - public static final int[] CHANNEL_SPACING = {118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] BASE_FREQUENCY = {140, 141, 142, 143, 144, 145, 146, 147, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 169, 170, 171, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195}; - - public ChannelIdentifierUpdateExplicit(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" IDEN:" + getIdentifier()); - - sb.append(" BASE:" + getBaseFrequency()); - - sb.append(" BW:" + getBandwidth()); - - sb.append(" SPACING:" + getChannelSpacing()); - - sb.append(" OFFSET:" + getTransmitOffset()); - - return sb.toString(); - } - - @Override - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - /** - * Channel bandwidth in hertz - */ - @Override - public int getBandwidth() - { - return mMessage.getInt(BANDWIDTH) * 125; - } - - @Override - public long getChannelSpacing() - { - return mMessage.getLong(CHANNEL_SPACING) * 125l; - } - - @Override - public long getBaseFrequency() - { - return mMessage.getLong(BASE_FREQUENCY) * 5l; - } - - @Override - public long getTransmitOffset() - { - return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l; - } - - @Override - public long getDownlinkFrequency(int channelNumber) - { - return getBaseFrequency() + (channelNumber * getChannelSpacing()); - } - - @Override - public long getUplinkFrequency(int channelNumber) - { - return getDownlinkFrequency(channelNumber) + getTransmitOffset(); - } - - @Override - public boolean isTDMA() - { - return false; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java deleted file mode 100644 index 3d8dd601..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.ExtendedFunction; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ExtendedFunctionCommand extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( ExtendedFunctionCommand.class ); - public static final int[] EXTENDED_FUNCTION = { 72,73,74,75,88,89,90,91,92, - 93,94,95,96,97,98,99 }; - public static final int[] ARGUMENT = { 112,113,114,115,116,117,118,119,120, - 121,122,123,136,137,138,139,140,141,142,143,144,145,145,147 }; - public static final int[] TARGET_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,190,192,193,194,195 }; - - public ExtendedFunctionCommand( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.EXTENDED_FUNCTION_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " EXTENDED FUNCTION:" + getExtendedFunction().getLabel() ); - - sb.append( " ARGUMENT: " + getTargetAddress() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public ExtendedFunction getExtendedFunction() - { - return ExtendedFunction.fromValue( mMessage.getInt( EXTENDED_FUNCTION ) ); - } - - public String getArgument() - { - return mMessage.getHex( ARGUMENT, 6 ); - } - - public String getSourceAddress() - { - switch( getExtendedFunction() ) - { - case RADIO_CHECK: - case RADIO_CHECK_ACK: - case RADIO_DETACH: - case RADIO_DETACH_ACK: - case RADIO_INHIBIT: - case RADIO_INHIBIT_ACK: - case RADIO_UNINHIBIT: - case RADIO_UNINHIBIT_ACK: - return getArgument(); - default: - break; - } - - return null; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java deleted file mode 100644 index aafe1d3b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupAffiliationQuery extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( GroupAffiliationQuery.class ); - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public GroupAffiliationQuery( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_AFFILIATION_QUERY.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java deleted file mode 100644 index c350c3da..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -import java.util.Set; -import java.util.TreeSet; - -public class GroupVoiceChannelUpdate extends TDULinkControlMessage implements IdentifierReceiver -{ - public static final int[] IDENTIFIER_A = {72, 73, 74, 75}; - public static final int[] CHANNEL_A = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99}; - public static final int[] GROUP_ADDRESS_A = {112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] IDENTIFIER_B = {140, 141, 142, 143}; - public static final int[] CHANNEL_B = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; - public static final int[] GROUP_ADDRESS_B = {168, 169, 170, 171, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mIdentifierUpdateA; - private IBandIdentifier mIdentifierUpdateB; - - public GroupVoiceChannelUpdate(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" CHAN A:" + getChannelA()); - - sb.append(" GRP A:" + getGroupAddressA()); - - if(hasChannelB()) - { - sb.append(" CHAN B:" + getChannelA()); - - sb.append(" GRP B:" + getGroupAddressA()); - } - - return sb.toString(); - } - - public int getChannelIdentifierA() - { - return mMessage.getInt(IDENTIFIER_A); - } - - public int getChannelNumberA() - { - return mMessage.getInt(CHANNEL_A); - } - - public String getChannelA() - { - return getChannelIdentifierA() + "-" + getChannelNumberA(); - } - - public int getChannelIdentifierB() - { - return mMessage.getInt(IDENTIFIER_B); - } - - public int getChannelNumberB() - { - return mMessage.getInt(CHANNEL_B); - } - - public String getChannelB() - { - return getChannelIdentifierB() + "-" + getChannelNumberB(); - } - - public boolean hasChannelB() - { - return getChannelNumberB() != 0; - } - - public String getGroupAddressA() - { - return mMessage.getHex(GROUP_ADDRESS_A, 4); - } - - public String getGroupAddressB() - { - return mMessage.getHex(GROUP_ADDRESS_B, 4); - } - - /** - * Returns a sorted list of addresses contained in the A and B fields - * - * @return - */ - public Set getAddresses() - { - Set addresses = new TreeSet(); - - addresses.add(getGroupAddressA()); - addresses.add(getGroupAddressB()); - - return addresses; - } - - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getChannelIdentifierA()) - { - mIdentifierUpdateA = message; - } - if(identifier == getChannelIdentifierB()) - { - mIdentifierUpdateB = message; - } - } - - @Override - public int[] getIdentifiers() - { - if(hasChannelB()) - { - int[] identifiers = new int[2]; - identifiers[0] = getChannelIdentifierA(); - identifiers[1] = getChannelIdentifierB(); - - return identifiers; - } - else - { - int[] identifiers = new int[1]; - identifiers[0] = getChannelIdentifierA(); - - return identifiers; - } - } - - public long getDownlinkFrequencyA() - { - if(mIdentifierUpdateA != null) - { - return mIdentifierUpdateA.getDownlinkFrequency(getChannelNumberA()); - } - - return 0; - } - - public long getUplinkFrequencyA() - { - if(mIdentifierUpdateA != null) - { - return mIdentifierUpdateA.getUplinkFrequency(getChannelNumberA()); - } - - return 0; - } - - public long getDownlinkFrequencyB() - { - if(mIdentifierUpdateB != null) - { - return mIdentifierUpdateB.getDownlinkFrequency(getChannelNumberB()); - } - - return 0; - } - - public long getUplinkFrequencyB() - { - if(mIdentifierUpdateB != null) - { - return mIdentifierUpdateB.getUplinkFrequency(getChannelNumberB()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java deleted file mode 100644 index 55a6fb8d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java +++ /dev/null @@ -1,176 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupVoiceChannelUpdateExplicit extends TDULinkControlMessage - implements IdentifierReceiver -{ - private final static Logger mLog = - LoggerFactory.getLogger(GroupVoiceChannelUpdateExplicit.class); - - public static final int EMERGENCY_FLAG = 92; - public static final int ENCRYPTED_CHANNEL_FLAG = 93; - public static final int DUPLEX_MODE = 94; - public static final int SESSION_MODE = 95; - public static final int[] GROUP_ADDRESS = {112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] TRANSMIT_IDENTIFIER = {140, 141, 142, 143}; - public static final int[] TRANSMIT_CHANNEL = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; - public static final int[] RECEIVE_IDENTIFIER = {168, 169, 170, 171}; - public static final int[] RECEIVE_CHANNEL = {184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupVoiceChannelUpdateExplicit(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - if(isEncryptedChannel()) - { - sb.append(" ENCRYPTED CHANNEL"); - } - - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncryptedChannel() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getGroupAddress() - { - return mMessage.getHex(GROUP_ADDRESS, 4); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitChannelIdentifier(); - identifiers[1] = getReceiveChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java deleted file mode 100644 index daa3ab0a..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupVoiceChannelUser extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( GroupVoiceChannelUser.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 92; - public static final int ENCRYPTED_CHANNEL_FLAG = 93; - public static final int DUPLEX_MODE = 94; - public static final int SESSION_MODE = 95; - public static final int[] GROUP_ADDRESS = { 120,121,122,123,136,137,138,139, - 140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public GroupVoiceChannelUser( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.GROUP_VOICE_CHANNEL_USER.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getGroupAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/MessageUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/MessageUpdate.java deleted file mode 100644 index 39bc6607..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/MessageUpdate.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageUpdate extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( MessageUpdate.class ); - public static final int[] SHORT_DATA_MESSAGE = { 72,73,74,75,88,89,90,91,92, - 93,94,95,96,97,98,99 }; - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,190,192,193,194,195 }; - - public MessageUpdate( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.MESSAGE_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " MSG:" + getShortDataMessage() ); - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getShortDataMessage() - { - return mMessage.getHex( SHORT_DATA_MESSAGE, 4 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java deleted file mode 100644 index 1d7cf390..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Service; - -public class NetworkStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver -{ - public static final int[] WACN = {72, 73, 74, 75, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 112, 113, 114, - 115, 116, 117, 118, 119, 120, 121, 122, 123}; - public static final int[] SYSTEM = {136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147}; - public static final int[] IDENTIFIER = {160, 161, 162, 163}; - public static final int[] CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; - public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mIdentifierUpdate; - - public NetworkStatusBroadcast(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.NETWORK_STATUS_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" NETWORK:" + getWACN()); - sb.append(" SYS:" + getSystem()); - sb.append(" CHAN:" + getChannel()); - sb.append(" " + Service.getServices(getSystemServiceClass()).toString()); - - return sb.toString(); - } - - public String getWACN() - { - return mMessage.getHex(WACN, 5); - } - - public String getSystem() - { - return mMessage.getHex(SYSTEM, 3); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java deleted file mode 100644 index 16b51973..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class NetworkStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver -{ - public static final int[] WACN = {72, 73, 74, 75, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 112, 113, 114, 115}; - public static final int[] SYSTEM = {116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] TRANSMIT_IDENTIFIER = {140, 141, 142, 143}; - public static final int[] TRANSMIT_CHANNEL = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; - public static final int[] RECEIVE_IDENTIFIER = {168, 169, 170, 171}; - public static final int[] RECEIVE_CHANNEL = {184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public NetworkStatusBroadcastExplicit(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.NETWORK_STATUS_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" NETWORK:" + getWACN()); - sb.append(" SYS:" + getSystem()); - sb.append(" TRANSMIT:" + getTransmitChannel()); - sb.append(" RECEIVE:" + getReceiveChannel()); - - return sb.toString(); - } - - public String getWACN() - { - return mMessage.getHex(WACN, 5); - } - - public String getSystem() - { - return mMessage.getHex(SYSTEM, 3); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java deleted file mode 100644 index 2343bf2b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.Encryption; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ProtectionParameterBroadcast extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( ProtectionParameterBroadcast.class ); - - public final static int[] ALGORITHM_ID = { 112,113,114,115,116,117,118,119 }; - public final static int[] KEY_ID = { 120,121,122,123,136,137,138,139,140, - 141,142,143,144,145,146,147 }; - public static final int[] TARGET_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,190,192,193,194,195 }; - - public ProtectionParameterBroadcast( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.PROTECTION_PARAMETER_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ENCRYPTION:" + getEncryption().name() ); - sb.append( " KEY:" + getEncryptionKey() ); - sb.append( " ADDRESSS:" + getTargetAddress() ); - - return sb.toString(); - } - - public Encryption getEncryption() - { - return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); - } - - public int getEncryptionKey() - { - return mMessage.getInt( KEY_ID ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java deleted file mode 100644 index 8e556b7d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class RFSSStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver -{ - public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; - public static final int[] SYSTEM_ID = {96, 97, 98, 99, 112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; - public static final int[] IDENTIFIER = {160, 161, 162, 163}; - public static final int[] CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; - public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mIdentifierUpdate; - - public RFSSStatusBroadcast(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.RFSS_STATUS_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" SYS:" + getSystem()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" CHAN:" + getChannelNumber()); - - sb.append(" " + SystemService.toString(getSystemServiceClass())); - - sb.append(" " + mMessage.toString()); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LRA, 2); - } - - public String getSystem() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java deleted file mode 100644 index 40fcdf77..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RFSSStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver -{ - private final static Logger mLog = LoggerFactory.getLogger(RFSSStatusBroadcastExplicit.class); - - public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; - public static final int[] RECEIVE_IDENTIFIER = {92, 93, 94, 95}; - public static final int[] RECEIVE_CHANNEL = {96, 97, 98, 99, 112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; - public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; - public static final int[] TRANSMIT_CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; - public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public RFSSStatusBroadcastExplicit(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.RFSS_STATUS_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" TRANSMIT:" + getTransmitChannelNumber()); - - sb.append(" RECEIVE:" + getReceiveChannelNumber()); - - sb.append(" " + SystemService.toString(getSystemServiceClass())); - - sb.append(" " + mMessage.toString()); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LRA, 2); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java deleted file mode 100644 index 988eb294..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class SecondaryControlChannelBroadcast extends TDULinkControlMessage implements IdentifierReceiver -{ - public static final int[] RFSS_ID = {72, 73, 74, 75, 88, 89, 90, 91}; - public static final int[] SITE_ID = {92, 93, 94, 95, 96, 97, 98, 99}; - public static final int[] IDENTIFIER_A = {112, 113, 114, 115}; - public static final int[] CHANNEL_A = {116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] SYSTEM_SERVICE_CLASS_A = {140, 141, 142, 143, 144, 145, 146, 147}; - public static final int[] IDENTIFIER_B = {160, 161, 162, 163}; - public static final int[] CHANNEL_B = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; - public static final int[] SYSTEM_SERVICE_CLASS_B = {188, 189, 190, 191, 192, 193, 194, 195}; - - private IBandIdentifier mIdentifierProviderA; - private IBandIdentifier mIdentifierProviderB; - - public SecondaryControlChannelBroadcast(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" CHAN A:" + getChannelA()); - - sb.append(" " + SystemService.toString(getSystemServiceClassA())); - - sb.append(" CHAN B:" + getChannelB()); - - sb.append(" " + SystemService.toString(getSystemServiceClassB())); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifierA() - { - return mMessage.getInt(IDENTIFIER_A); - } - - public int getChannelA() - { - return mMessage.getInt(CHANNEL_A); - } - - public String getChannelNumberA() - { - return getIdentifierA() + "-" + getChannelA(); - } - - public int getIdentifierB() - { - return mMessage.getInt(IDENTIFIER_B); - } - - public int getChannelB() - { - return mMessage.getInt(CHANNEL_B); - } - - public String getChannelNumberB() - { - return getIdentifierB() + "-" + getChannelB(); - } - - public int getSystemServiceClassA() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS_A); - } - - public int getSystemServiceClassB() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS_B); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getIdentifierA()) - { - mIdentifierProviderA = message; - } - if(identifier == getIdentifierB()) - { - mIdentifierProviderB = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getIdentifierA(); - identifiers[1] = getIdentifierB(); - - return identifiers; - } - - public long getDownlinkFrequencyA() - { - if(mIdentifierProviderA != null) - { - return mIdentifierProviderA.getDownlinkFrequency(getChannelA()); - } - - return 0; - } - - public long getUplinkFrequencyA() - { - if(mIdentifierProviderA != null) - { - return mIdentifierProviderA.getUplinkFrequency(getChannelA()); - } - - return 0; - } - - public long getDownlinkFrequencyB() - { - if(mIdentifierProviderB != null) - { - return mIdentifierProviderB.getDownlinkFrequency(getChannelB()); - } - - return 0; - } - - public long getUplinkFrequencyB() - { - if(mIdentifierProviderB != null) - { - return mIdentifierProviderB.getUplinkFrequency(getChannelB()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java deleted file mode 100644 index 705880bf..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java +++ /dev/null @@ -1,156 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemService; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; - -public class SecondaryControlChannelBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver -{ - public static final int[] RFSS_ID = {72, 73, 74, 75, 88, 89, 90, 91}; - public static final int[] SITE_ID = {92, 93, 94, 95, 96, 97, 98, 99}; - public static final int[] TRANSMIT_IDENTIFIER = {112, 113, 114, 115}; - public static final int[] TRANSMIT_CHANNEL = {116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; - public static final int[] RECEIVE_IDENTIFIER = {140, 141, 142, 143}; - public static final int[] RECEIVE_CHANNEL = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; - public static final int[] SYSTEM_SERVICE_CLASS = {168, 169, 170, 171, 184, 185, 186, 187}; - - private IBandIdentifier mTransmitIdentifierProvider; - private IBandIdentifier mReceiveIdentifierProvider; - - public SecondaryControlChannelBroadcastExplicit(TDULinkControlMessage source) - { - super(source); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); - - sb.append(" TRANSMIT:" + getTransmitChannel()); - - sb.append(" RECEIVE:" + getReceiveChannel()); - - sb.append(" " + SystemService.toString(getSystemServiceClass())); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_CHANNEL); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_CHANNEL); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitIdentifier()) - { - mTransmitIdentifierProvider = message; - } - if(identifier == getReceiveIdentifier()) - { - mReceiveIdentifierProvider = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitIdentifier(); - identifiers[1] = getReceiveIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierProvider != null) - { - return mTransmitIdentifierProvider.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierProvider != null) - { - return mReceiveIdentifierProvider.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusQuery.java deleted file mode 100644 index 0c4acefd..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusQuery.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class StatusQuery extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( StatusQuery.class ); - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public StatusQuery( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.STATUS_QUERY.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusUpdate.java deleted file mode 100644 index 239aa5de..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/StatusUpdate.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class StatusUpdate extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( StatusUpdate.class ); - public static final int[] USER_STATUS = { 80,81,82,83,84,85,86,87 }; - public static final int[] UNIT_STATUS = { 88,89,90,91,92,93,94,95 }; - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public StatusUpdate( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.STATUS_UPDATE.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " STATUS USER:" + getUserStatus() ); - sb.append( " UNIT:" + getUnitStatus() ); - sb.append( " FROM:" + getSourceAddress() ); - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public String getUserStatus() - { - return mMessage.getHex( USER_STATUS, 2 ); - } - - public String getUnitStatus() - { - return mMessage.getHex( UNIT_STATUS, 2 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java deleted file mode 100644 index 31d781a3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Service; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -public class SystemServiceBroadcast extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( SystemServiceBroadcast.class ); - public static final int[] REQUEST_PRIORITY_LEVEL = { 96,97,98,99 }; - public static final int[] AVAILABLE_SERVICES = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SUPPORTED_SERVICES = { 160,161,162,163,164,165, - 166,167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public SystemServiceBroadcast( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.SYSTEM_SERVICE_BROADCAST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SERVICES AVAILABLE " ); - - sb.append( getAvailableServices() ); - - sb.append( " SUPPORTED " ); - - sb.append( getSupportedServices() ); - - return sb.toString(); - } - - public List getAvailableServices() - { - long bitmap = mMessage.getLong( AVAILABLE_SERVICES ); - - return Service.getServices( bitmap ); - } - - public List getSupportedServices() - { - long bitmap = mMessage.getLong( SUPPORTED_SERVICES ); - - return Service.getServices( bitmap ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java deleted file mode 100644 index 8e40d0a1..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.Vendor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class TDULCMessageFactory -{ - private final static Logger mLog = - LoggerFactory.getLogger( TDULCMessageFactory.class ); - - /** - * Constructs a new sub-class of the TDULC message if the opcode is recognized - * or returns the original message. - */ - public static TDULinkControlMessage getMessage( TDULinkControlMessage message ) - { - if( message.isImplicitFormat() || message.getVendor() == Vendor.STANDARD ) - { - switch( message.getOpcode() ) - { - case ADJACENT_SITE_STATUS_BROADCAST: - return new AdjacentSiteStatusBroadcast( message ); - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - return new AdjacentSiteStatusBroadcastExplicit( message ); - case CALL_ALERT: - return new CallAlert( message ); - case CALL_TERMINATION_OR_CANCELLATION: - return new CallTermination( message ); - case CHANNEL_IDENTIFIER_UPDATE: - return new ChannelIdentifierUpdate( message ); - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - return new ChannelIdentifierUpdateExplicit( message ); - case EXTENDED_FUNCTION_COMMAND: - return new ExtendedFunctionCommand( message ); - case GROUP_AFFILIATION_QUERY: - return new GroupAffiliationQuery( message ); - case GROUP_VOICE_CHANNEL_UPDATE: - return new GroupVoiceChannelUpdate( message ); - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - return new GroupVoiceChannelUpdateExplicit( message ); - case GROUP_VOICE_CHANNEL_USER: - return new GroupVoiceChannelUser( message ); - case MESSAGE_UPDATE: - return new MessageUpdate( message ); - case NETWORK_STATUS_BROADCAST: - return new NetworkStatusBroadcast( message ); - case NETWORK_STATUS_BROADCAST_EXPLICIT: - return new NetworkStatusBroadcastExplicit( message ); - case PROTECTION_PARAMETER_BROADCAST: - return new ProtectionParameterBroadcast( message ); - case RFSS_STATUS_BROADCAST: - return new RFSSStatusBroadcast( message ); - case RFSS_STATUS_BROADCAST_EXPLICIT: - return new RFSSStatusBroadcastExplicit( message ); - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - return new SecondaryControlChannelBroadcast( message ); - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - return new SecondaryControlChannelBroadcastExplicit( message ); - case STATUS_QUERY: - return new StatusQuery( message ); - case STATUS_UPDATE: - return new StatusUpdate( message ); - case SYSTEM_SERVICE_BROADCAST: - return new SystemServiceBroadcast( message ); - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - return new TelephoneInterconnectAnswerRequest( message ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - return new TelephoneInterconnectVoiceChannelUser( message ); - case UNIT_AUTHENTICATION_COMMAND: - return new UnitAuthenticationCommand( message ); - case UNIT_REGISTRATION_COMMAND: - return new UnitRegistrationCommand( message ); - case UNIT_TO_UNIT_ANSWER_REQUEST: - return new UnitToUnitAnswerRequest( message ); - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - return new UnitToUnitVoiceChannelUser( message ); - default: - break; - } - } - else - { - switch( message.getVendor() ) - { - case MOTOROLA: - default: - break; - } - } - - return message; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java deleted file mode 100644 index 1429e2d6..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java +++ /dev/null @@ -1,282 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.edac.Golay24; -import io.github.dsheirer.edac.ReedSolomon_63_47_17; -import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.MotorolaOpcode; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import io.github.dsheirer.module.decode.p25.reference.Vendor; -import io.github.dsheirer.module.decode.p25.reference.VendorLinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TDULinkControlMessage extends P25Message -{ - public final static Logger mLog = LoggerFactory.getLogger( TDULinkControlMessage.class ); - - public static final int[] LC_HEX_0 = { 64,65,66,67,68,69 }; - public static final int[] LC_HEX_1 = { 70,71,72,73,74,75 }; - public static final int[] LC_HEX_2 = { 88,89,90,91,92,93 }; - public static final int[] LC_HEX_3 = { 94,95,96,97,98,99 }; - public static final int[] LC_HEX_4 = { 112,113,114,115,116,117 }; - public static final int[] LC_HEX_5 = { 118,119,120,121,122,123 }; - public static final int[] LC_HEX_6 = { 136,137,138,139,140,141 }; - public static final int[] LC_HEX_7 = { 142,143,144,145,146,147 }; - public static final int[] LC_HEX_8 = { 160,161,162,163,164,165 }; - public static final int[] LC_HEX_9 = { 166,167,168,169,170,171 }; - public static final int[] LC_HEX_10 = { 184,185,186,187,188,189 }; - public static final int[] LC_HEX_11 = { 190,191,192,193,194,195 }; - public static final int[] RS_HEX_0 = { 208,209,210,211,212,213 }; - public static final int[] RS_HEX_1 = { 214,215,216,217,218,219 }; - public static final int[] RS_HEX_2 = { 232,233,234,235,236,237 }; - public static final int[] RS_HEX_3 = { 238,239,240,241,242,243 }; - public static final int[] RS_HEX_4 = { 256,257,258,259,260,261 }; - public static final int[] RS_HEX_5 = { 262,263,264,265,266,267 }; - public static final int[] RS_HEX_6 = { 280,281,282,283,284,285 }; - public static final int[] RS_HEX_7 = { 286,287,288,289,290,291 }; - public static final int[] RS_HEX_8 = { 304,305,306,307,308,309 }; - public static final int[] RS_HEX_9 = { 310,311,312,313,314,315 }; - public static final int[] RS_HEX_10 = { 328,329,330,331,332,333 }; - public static final int[] RS_HEX_11 = { 334,335,336,337,338,339 }; - - public static final int ENCRYPTION_FLAG = 64; - public static final int IMPLICIT_VENDOR_FLAG = 65; - public static final int[] OPCODE = { 66,67,68,69,70,71 }; - public static final int[] VENDOR = { 72,73,74,75,88,89,90,91 }; - - /* Reed-Solomon(24,12,13) code protects the link control word. Maximum - * correctable errors are: Hamming Distance(13) / 2 = 6 */ - public static final ReedSolomon_63_47_17 mReedSolomonDecoder = - new ReedSolomon_63_47_17( 6 ); - - public TDULinkControlMessage( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - - /* NID CRC is checked in the message framer so a constructed message - * means it passed the CRC */ - mCRC = new CRC[ 3 ]; - mCRC[ 0 ] = CRC.PASSED; - - checkCRC(); - } - - /** - * Constructs a TDULinkControl message from an existing message and bypasses - * the CRC calculation. - */ - protected TDULinkControlMessage( TDULinkControlMessage message ) - { - super( message.getSourceMessage(), DataUnitID.TDULC, message.getAliasList() ); - - mCRC = message.getCRC(); - } - - public CRC[] getCRC() - { - return mCRC; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - public String getMessageStub() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - sb.append( " " ); - - if( isEncrypted() ) - { - sb.append( "ENCRYPTED" ); - } - else - { - if( isImplicitFormat() || getVendor() == Vendor.STANDARD ) - { - sb.append( getOpcode().getLabel() ); - } - else - { - sb.append( "VENDOR:" + getVendor().getLabel() + " " ); - sb.append( getVendorOpcode().getLabel() ); - } - } - - return sb.toString(); - } - - public boolean isEncrypted() - { - return mMessage.get( ENCRYPTION_FLAG ); - } - - public boolean isImplicitFormat() - { - return mMessage.get( IMPLICIT_VENDOR_FLAG ); - } - - /** - * Standard opcodes when the Vendor is STANDARD - */ - public LinkControlOpcode getOpcode() - { - return LinkControlOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - /** - * Returns a generic vendor-neutral opcode. Use this when there is a non - * standard vendor code. - */ - public VendorLinkControlOpcode getVendorOpcode() - { - return VendorLinkControlOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - /** - * If the vendor is motorola, returns the Motorola OPCODE for this message - */ - public MotorolaOpcode getMotorolaOpcode() - { - return MotorolaOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - public Vendor getVendor() - { - if( isImplicitFormat() ) - { - return Vendor.STANDARD; - } - - return Vendor.fromValue( mMessage.getInt( VENDOR ) ); - } - - - /** - * Checks and repairs the 12 x 24-bit golay(24,12,8) codewords and then - * checks and repairs the 24 x 6-bit link control and reed-solomon - * RS(24,12,13) parity codewords - */ - private void checkCRC() - { - /* Check the Golay codewords */ - int x = 64; - - mCRC[ 1 ] = CRC.PASSED; - - while( x < mMessage.size() ) - { - mMessage = Golay24.checkAndCorrect( mMessage, x ); - - if( !( mMessage.getCRC() == CRC.PASSED ) ) - { - mCRC[ 1 ] = CRC.FAILED_CRC; - } - - x += 24; - } - - /* Check the Reed-Solomon parity bits. The RS decoder expects the link - * control data and reed solomon parity hex codewords in reverse order. - * The RS(24,12,13) code used by P25 removes the left-hand 47 data hex - * words, so we replace them with zeros. */ - int[] input = new int [63]; - int[] output = new int [63]; - - input[ 0 ] = mMessage.getInt( RS_HEX_11 ); - input[ 1 ] = mMessage.getInt( RS_HEX_10 ); - input[ 2 ] = mMessage.getInt( RS_HEX_9 ); - input[ 3 ] = mMessage.getInt( RS_HEX_8 ); - input[ 4 ] = mMessage.getInt( RS_HEX_7 ); - input[ 5 ] = mMessage.getInt( RS_HEX_6 ); - input[ 6 ] = mMessage.getInt( RS_HEX_5 ); - input[ 7 ] = mMessage.getInt( RS_HEX_4 ); - input[ 8 ] = mMessage.getInt( RS_HEX_3 ); - input[ 9 ] = mMessage.getInt( RS_HEX_2 ); - input[ 10 ] = mMessage.getInt( RS_HEX_1 ); - input[ 11 ] = mMessage.getInt( RS_HEX_0 ); - input[ 12 ] = mMessage.getInt( LC_HEX_11 ); - input[ 13 ] = mMessage.getInt( LC_HEX_10 ); - input[ 14 ] = mMessage.getInt( LC_HEX_9 ); - input[ 15 ] = mMessage.getInt( LC_HEX_8 ); - input[ 16 ] = mMessage.getInt( LC_HEX_7 ); - input[ 17 ] = mMessage.getInt( LC_HEX_6 ); - input[ 18 ] = mMessage.getInt( LC_HEX_5 ); - input[ 19 ] = mMessage.getInt( LC_HEX_4 ); - input[ 20 ] = mMessage.getInt( LC_HEX_3 ); - input[ 21 ] = mMessage.getInt( LC_HEX_2 ); - input[ 22 ] = mMessage.getInt( LC_HEX_1 ); - input[ 23 ] = mMessage.getInt( LC_HEX_0 ); - /* indexes 24 - 62 are defaulted to zero */ - - boolean irrecoverableErrors = mReedSolomonDecoder.decode( input, output ); - - if( irrecoverableErrors ) - { - mCRC[ 2 ] = CRC.FAILED_CRC; - } - else - { - mCRC[ 2 ] = CRC.PASSED; - - /* Only fix the link control data hex codewords */ - repairHexCodeword( input, output, 12, LC_HEX_11 ); - repairHexCodeword( input, output, 13, LC_HEX_10 ); - repairHexCodeword( input, output, 14, LC_HEX_9 ); - repairHexCodeword( input, output, 15, LC_HEX_8 ); - repairHexCodeword( input, output, 16, LC_HEX_7 ); - repairHexCodeword( input, output, 17, LC_HEX_6 ); - repairHexCodeword( input, output, 18, LC_HEX_5 ); - repairHexCodeword( input, output, 19, LC_HEX_4 ); - repairHexCodeword( input, output, 20, LC_HEX_3 ); - repairHexCodeword( input, output, 21, LC_HEX_2 ); - repairHexCodeword( input, output, 22, LC_HEX_1 ); - repairHexCodeword( input, output, 23, LC_HEX_0 ); - - /* If the golay check failed, mark it as corrected too */ - if( mCRC[ 1 ] == CRC.FAILED_CRC ) - { - mCRC[ 1 ] = CRC.CORRECTED; - } - } - } - - private void repairHexCodeword( int[] input, int[] output, int index, int[] indexSet ) - { - if( input[ index ] != output[ index ] ) - { - mMessage.load( indexSet[ 0 ], 6, output[ index ] ); - mCRC[ 2 ] = CRC.CORRECTED; - } - } - - /** - * Indicates if the message passed the golay and reed-solomon parity checks - * or was corrected. - */ - public boolean isValid() - { - // mCRC[ 0 ] - always passes (otherwise message wouldn't be constructed ) - // mCRC[ 1 ] - golay - always passes or corrected if mCRC[2] is valid - - return mCRC[ 2 ] == CRC.PASSED || mCRC[ 2 ] == CRC.CORRECTED; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java deleted file mode 100644 index 503b1ed3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.Digit; -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -public class TelephoneInterconnectAnswerRequest extends TDULinkControlMessage -{ - private final static Logger mLog = LoggerFactory.getLogger( TelephoneInterconnectAnswerRequest.class ); - - public static final int[] DIGIT_1 = { 72,73,74,75 }; - public static final int[] DIGIT_2 = { 88,89,90,91 }; - public static final int[] DIGIT_3 = { 92,93,94,95 }; - public static final int[] DIGIT_4 = { 96,97,98,99 }; - public static final int[] DIGIT_5 = { 112,113,114,115 }; - public static final int[] DIGIT_6 = { 116,117,118,119 }; - public static final int[] DIGIT_7 = { 120,121,122,123 }; - public static final int[] DIGIT_8 = { 136,137,138,139 }; - public static final int[] DIGIT_9 = { 140,141,142,143 }; - public static final int[] DIGIT_10 = { 144,145,146,147 }; - - public static final int[] TARGET_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public TelephoneInterconnectAnswerRequest( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.TELEPHONE_INTERCONNECT_ANSWER_REQUEST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " TEL:" ); - sb.append( getTelephoneNumber() ); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getTelephoneNumber() - { - List digits = new ArrayList(); - - digits.add( mMessage.getInt( DIGIT_1 ) ); - digits.add( mMessage.getInt( DIGIT_2 ) ); - digits.add( mMessage.getInt( DIGIT_3 ) ); - digits.add( mMessage.getInt( DIGIT_4 ) ); - digits.add( mMessage.getInt( DIGIT_5 ) ); - digits.add( mMessage.getInt( DIGIT_6 ) ); - digits.add( mMessage.getInt( DIGIT_7 ) ); - digits.add( mMessage.getInt( DIGIT_8 ) ); - digits.add( mMessage.getInt( DIGIT_9 ) ); - digits.add( mMessage.getInt( DIGIT_10 ) ); - - return Digit.decode( digits ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java deleted file mode 100644 index b969b3cc..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TelephoneInterconnectVoiceChannelUser extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( TelephoneInterconnectVoiceChannelUser.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 92; - public static final int ENCRYPTED_CHANNEL_FLAG = 93; - public static final int DUPLEX_MODE = 94; - public static final int SESSION_MODE = 95; - - public static final int[] CALL_TIMER = { 120,121,122,123,136,137,138,139, - 140,141,142,143,144,145,146,147 }; - public static final int[] ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public TelephoneInterconnectVoiceChannelUser( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " TIMER:" + getCallTimer() + " SECS" ); - - sb.append( " ADDR:" + getAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getAddress() - { - return mMessage.getHex( ADDRESS, 6 ); - } - - /** - * Call timer in seconds - */ - public int getCallTimer() - { - int units = mMessage.getInt( CALL_TIMER ); - - return (int)( units / 10 ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java deleted file mode 100644 index 6a458d26..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitAuthenticationCommand extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitAuthenticationCommand.class ); - public static final int[] NETWORK_ID = { 72,73,74,75,88,89,90,91,92,93,94, - 95,96,97,98,99,112,113,114,115 }; - public static final int[] SYSTEM_ID = { 116,117,118,119,120,121,122,123, - 136,137,138,139 }; - /* ICD says this is a source id, but this is a command messages, so this - * must be a target id */ - public static final int[] TARGET_ID = { 140,141,142,143,144,145,146,147, - 160,161,162,163,164,165,166,167,168,169,170,171,184,185,186,187 }; - - public UnitAuthenticationCommand( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_AUTHENTICATION_COMMAND.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ADDRESS:" + getCompleteTargetAddress() ); - - return sb.toString(); - } - - public String getCompleteTargetAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getNetworkID() ); - sb.append( ":" ); - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getTargetID() ); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex( NETWORK_ID, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getTargetID() - { - return mMessage.getHex( TARGET_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java deleted file mode 100644 index 145f237d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitRegistrationCommand extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitRegistrationCommand.class ); - public static final int[] NETWORK_ID = { 72,73,74,75,88,89,90,91,92,93,94, - 95,96,97,98,99,112,113,114,115 }; - public static final int[] SYSTEM_ID = { 116,117,118,119,120,121,122,123, - 136,137,138,139 }; - /* ICD calls this source, but should be target address */ - public static final int[] TARGET_ID = { 140,141,142,143,144,145,146,147, - 160,161,162,163,164,165,166,167,168,169,170,171,184,185,186,187 }; - - public UnitRegistrationCommand( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_REGISTRATION_COMMAND.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ADDRESS:" + getCompleteTargetAddress() ); - - return sb.toString(); - } - - public String getCompleteTargetAddress() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getNetworkID() ); - sb.append( ":" ); - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getTargetID() ); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex( NETWORK_ID, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getTargetID() - { - return mMessage.getHex( TARGET_ID, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java deleted file mode 100644 index e6cd06d7..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitToUnitAnswerRequest extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitToUnitAnswerRequest.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 92; - public static final int ENCRYPTED_CHANNEL_FLAG = 93; - public static final int DUPLEX_MODE = 94; - public static final int SESSION_MODE = 95; - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public UnitToUnitAnswerRequest( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_TO_UNIT_ANSWER_REQUEST.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java deleted file mode 100644 index 7789e094..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tdu.lc; - -import io.github.dsheirer.module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UnitToUnitVoiceChannelUser extends TDULinkControlMessage -{ - private final static Logger mLog = - LoggerFactory.getLogger( UnitToUnitVoiceChannelUser.class ); - /* Service Options */ - public static final int EMERGENCY_FLAG = 92; - public static final int ENCRYPTED_CHANNEL_FLAG = 93; - public static final int DUPLEX_MODE = 94; - public static final int SESSION_MODE = 95; - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; - - public UnitToUnitVoiceChannelUser( TDULinkControlMessage source ) - { - super( source ); - } - - @Override - public String getEventType() - { - return LinkControlOpcode.UNIT_TO_UNIT_VOICE_CHANNEL_USER.getDescription(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } - - sb.append( " FROM:" + getSourceAddress() ); - - sb.append( " TO:" + getTargetAddress() ); - - return sb.toString(); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ChannelGrant.java deleted file mode 100644 index 9fad0b82..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ChannelGrant.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class ChannelGrant extends ServiceMessage implements IdentifierReceiver -{ - public static final int[] PRIORITY = {85, 86, 87}; - public static final int[] CHANNEL_IDENTIFIER = {88, 89, 90, 91}; - public static final int[] CHANNEL_NUMBER = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; - - private IBandIdentifier mIdentifierUpdate; - - public ChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - /** - * Indicates if this channel grant is for a TDMA channel - */ - public boolean isTDMAChannel() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.isTDMA(); - } - - return false; - } - - /** - * 1 = Lowest, 4 = Default, 7 = Highest - */ - public int getPriority() - { - return mMessage.getInt(PRIORITY); - } - - public int getChannelIdentifier() - { - return mMessage.getInt(CHANNEL_IDENTIFIER); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL_NUMBER); - } - - public String getChannel() - { - return getChannelIdentifier() + "-" + getChannelNumber(); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - identifiers[0] = getChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrant.java deleted file mode 100644 index 6f1b3d48..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrant.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class GroupChannelGrant extends ChannelGrant -{ - public static final int[] GROUP_ADDRESS = { 104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119 }; - - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public GroupChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " SOURCE UNIT:" ); - sb.append( getSourceAddress() ); - - sb.append( " GROUP:" ); - sb.append( getGroupAddress() ); - - sb.append( " CHAN:" ); - sb.append( getChannelIdentifier() + "-" + getChannelNumber() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " UP:" + getUplinkFrequency() ); - - return sb.toString(); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - @Override - public Alias getFromIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getFromID() ); - } - - return null; - } - - @Override - public String getToID() - { - return getGroupAddress(); - } - - @Override - public Alias getToIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getGroupAddress() ); - } - - return null; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java deleted file mode 100644 index bf18e3bc..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java +++ /dev/null @@ -1,155 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class GroupChannelGrantExplicit extends ChannelGrant -{ - public static final int[] TRANSMIT_IDENTIFIER = {96, 97, 98, 99}; - public static final int[] TRANSMIT_NUMBER = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] RECEIVE_IDENTIFIER = {112, 113, 114, 115}; - public static final int[] RECEIVE_NUMBER = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; - public static final int[] GROUP_ADDRESS = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, - 142, 143}; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupChannelGrantExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public boolean isTDMAChannel() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.isTDMA(); - } - - return false; - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" XMIT:"); - sb.append(getTransmitChannelIdentifier() + "/" + getTransmitChannelNumber()); - - sb.append(" RCV:"); - sb.append(getReceiveChannelIdentifier() + "/" + getReceiveChannelNumber()); - - sb.append(" GRP:"); - sb.append(getGroupAddress()); - - return sb.toString(); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getGroupAddress() - { - return mMessage.getHex(GROUP_ADDRESS, 4); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mTransmitIdentifierUpdate = message; - } - - if(identifier == getReceiveChannelIdentifier()) - { - mReceiveIdentifierUpdate = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - identifiers[0] = getTransmitChannelIdentifier(); - identifiers[1] = getReceiveChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mTransmitIdentifierUpdate != null) - { - return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mReceiveIdentifierUpdate != null) - { - return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java deleted file mode 100644 index 4fd3d263..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java +++ /dev/null @@ -1,220 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class GroupMultiChannelGrant extends ChannelGrant implements IdentifierReceiver -{ - public static final int[] CHANNEL_IDENTIFIER_1 = {80, 81, 82, 83}; - public static final int[] CHANNEL_NUMBER_1 = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; - public static final int[] GROUP_ADDRESS_1 = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] CHANNEL_IDENTIFIER_2 = {112, 113, 114, 115}; - public static final int[] CHANNEL_NUMBER_2 = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; - public static final int[] GROUP_ADDRESS_2 = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate1; - private IBandIdentifier mIdentifierUpdate2; - - public GroupMultiChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" CHAN1:"); - sb.append(getChannelIdentifier1() + "/" + getChannelNumber1()); - - sb.append(" GRP1:"); - sb.append(getGroupAddress1()); - - if(hasChannelNumber2()) - { - sb.append(" CHAN2:"); - sb.append(getChannelIdentifier2() + "/" + getChannelNumber2()); - - sb.append(" GRP2:"); - sb.append(getGroupAddress2()); - } - - return sb.toString(); - } - - public int getChannelIdentifier1() - { - return mMessage.getInt(CHANNEL_IDENTIFIER_1); - } - - public int getChannelNumber1() - { - return mMessage.getInt(CHANNEL_NUMBER_1); - } - - public String getChannel1() - { - return getChannelIdentifier1() + "-" + getChannelNumber1(); - } - - public String getGroupAddress1() - { - return mMessage.getHex(GROUP_ADDRESS_1, 4); - } - - public int getChannelIdentifier2() - { - return mMessage.getInt(CHANNEL_IDENTIFIER_2); - } - - public int getChannelNumber2() - { - return mMessage.getInt(CHANNEL_NUMBER_2); - } - - public String getChannel2() - { - return getChannelIdentifier2() + "-" + getChannelNumber2(); - } - - public String getGroupAddress2() - { - return mMessage.getHex(GROUP_ADDRESS_2, 4); - } - - public boolean hasChannelNumber2() - { - return mMessage.getInt(CHANNEL_NUMBER_2) != - mMessage.getInt(CHANNEL_NUMBER_1); - } - - @Override - public String getFromID() - { - return getGroupAddress1(); - } - - @Override - public String getToID() - { - return getGroupAddress2(); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getChannelIdentifier1()) - { - mIdentifierUpdate1 = message; - } - - if(hasChannelNumber2() && identifier == getChannelIdentifier2()) - { - mIdentifierUpdate2 = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers; - - if(hasChannelNumber2()) - { - identifiers = new int[2]; - identifiers[0] = getChannelIdentifier1(); - identifiers[1] = getChannelIdentifier2(); - } - else - { - identifiers = new int[1]; - identifiers[0] = getChannelIdentifier1(); - } - - return identifiers; - } - - public long getDownlinkFrequency1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.getDownlinkFrequency(getChannelNumber1()); - } - - return 0; - } - - public boolean isTDMAChannel1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.isTDMA(); - } - - return false; - } - - public long getUplinkFrequency1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.getUplinkFrequency(getChannelNumber1()); - } - - return 0; - } - - public long getDownlinkFrequency2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.getDownlinkFrequency(getChannelNumber2()); - } - - return 0; - } - - public boolean isTDMAChannel2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.isTDMA(); - } - - return false; - } - - public long getUplinkFrequency2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.getUplinkFrequency(getChannelNumber2()); - } - - return 0; - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ISPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ISPMessage.java new file mode 100644 index 00000000..95ffa387 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ISPMessage.java @@ -0,0 +1,30 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Direction; + +/** + * P25 Inbound (ISP) TSBK Message + */ +public abstract class ISPMessage extends TSBKMessage +{ + /** + * Constructs an inbound (ISP) TSBK from the binary message sequence. + * + * @param dataUnitID TSBK1/2/3 + * @param message binary sequence + * @param nac decoded from the NID + * @param timestamp for the message + */ + public ISPMessage(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitID, message, nac, timestamp); + } + + @Override + public Direction getDirection() + { + return Direction.INBOUND; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/OSPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/OSPMessage.java new file mode 100644 index 00000000..4c33ded5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/OSPMessage.java @@ -0,0 +1,30 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Direction; + +/** + * P25 Outbound (OSP) TSBK Message + */ +public abstract class OSPMessage extends TSBKMessage +{ + /** + * Constructs an outbound (OSP) TSBK from the binary message sequence. + * + * @param dataUnitID TSBK1/2/3 + * @param message binary sequence + * @param nac decoded from the NID + * @param timestamp for the message + */ + public OSPMessage(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitID, message, nac, timestamp); + } + + @Override + public Direction getDirection() + { + return Direction.OUTBOUND; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java new file mode 100644 index 00000000..959536c1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java @@ -0,0 +1,296 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk; + +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.EnumSet; + +public enum Opcode +{ + //Vendor: standard, Outbound Service Packet (OSP) + OSP_GROUP_VOICE_CHANNEL_GRANT(0, "GRP_VCH_GRANT", "GROUP VOICE CHANNEL GRANT"), + OSP_RESERVED_01(1, "RESERVED_01", "OSP RESERVED 1"), + OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE(2, "GRP_VCH_GRNT_UPD", "GROUP VOICE CHANNEL GRANT UPDATE"), + OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT(3, "GRP_VCH_GRNT_UPX", "GROUP VOICE CHANNEL GRANT UPDATE EXPLICIT"), + OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT(4, "UU_VCH_GRANT", "UNIT-2-UNIT VOICE CHANNEL GRANT"), + OSP_UNIT_TO_UNIT_ANSWER_REQUEST(5, "UU_ANS_REQ", "UNIT-2-UNIT ANSWER REQUEST"), + OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE(6, "UU_VCH_GRANT_UPD", "UNIT-2-UNIT VOICE CHANNEL GRANT UPDATE"), + OSP_RESERVED_07(7, "RESERVED_07", "OSP RESERVED 7"), + OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT(8, "TEL_INT_VCH_GRNT", "TELEPHONE INTERCONNECT VOICE CHANNEL GRANT"), + OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE(9, "TEL_INT_VCH_GRNU", "TELEPHONE INTERCONNECT VOICE CHANNEL GRANT UPDATE"), + OSP_TELEPHONE_INTERCONNECT_ANSWER_REQUEST(10, "TEL_INT_ANS_RQST", "TELEPHONE INTERCONNECT ANSWER REQUEST"), + OSP_RESERVED_0B(11, "RESERVED_0B", "OSP RESERVED 11"), + OSP_RESERVED_0C(12, "RESERVED_0C", "OSP RESERVED 12"), + OSP_RESERVED_0D(13, "RESERVED_0D", "OSP RESERVED 13"), + OSP_RESERVED_0E(14, "RESERVED_0E", "OSP RESERVED 14"), + OSP_RESERVED_0F(15, "RESERVED_0F", "OSP RESERVED 15"), + OSP_INDIVIDUAL_DATA_CHANNEL_GRANT(16, "IND_DCH_GRANT", "INDIVIDUAL CHANNEL GRANT-OBSOLETE"), //OBSOLETE + OSP_GROUP_DATA_CHANNEL_GRANT(17, "GRP_DCH_GRANT", "GROUP DATA CHANNEL GRANT-OBSOLETE"), //OBSOLETE + OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT(18, "GRP_DCH_ANNOUNCE", "GROUP DATA CHANNEL ANNOUNCEMENT-OBSOLETE"), //OBSOLETE + OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT(19, "GRP_DCH_ANNC_EXP", "GROUP DATA CHANNEL ANNOUNCEMENT EXPLICIT-OBSOLETE"), //OBSOLETE + OSP_SNDCP_DATA_CHANNEL_GRANT(20, "SNDCP_DCH_GRANT", "SNDCP DATA CHANNEL GRANT"), + OSP_SNDCP_DATA_PAGE_REQUEST(21, "SNDCP_DCH_PAG_RQ", "SNDCP DATA CHANNEL PAGE REQUEST"), + OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT(22, "SNDCP_DCH_ANN_EX", "SNDCP DATA CHANNEL ANNOUNCEMENT EXPLICIT"), + OSP_RESERVED_17(23, "RESERVED_17", "OSP RESERVED 23"), + OSP_STATUS_UPDATE(24, "STATUS_UPDATE", "STATUS UPDATE"), + OSP_RESERVED_19(25, "RESERVED_19", "OSP RESERVED 25"), + OSP_STATUS_QUERY(26, "OSP_STATUS_QUERY", "STATUS QUERY"), + OSP_RESERVED_1B(27, "RESERVED_1B", "OSP RESERVED 27"), + OSP_MESSAGE_UPDATE(28, "MESSAGE_UPDATE", "MESSAGE UPDATE"), + OSP_RADIO_UNIT_MONITOR_COMMAND(29, "RADIO_MONITR_CMD", "RADIO MONITOR COMMAND"), + OSP_RESERVED_1E(30, "RESERVED_1E", "OSP RESERVED 30"), + OSP_CALL_ALERT(31, "CALL_ALERT", "CALL ALERT"), + OSP_ACKNOWLEDGE_RESPONSE(32, "ACK_RESPONSE_FNE", "ACKNOWLEDGE RESPONSE"), + OSP_QUEUED_RESPONSE(33, "QUEUED_RESPONSE", "QUEUED RESPONSE"), + OSP_RESERVED_22(34, "RESERVED_22", "OSP RESERVED 34"), + OSP_RESERVED_23(35, "RESERVED_23", "OSP RESERVED 35"), + OSP_EXTENDED_FUNCTION_COMMAND(36, "EXTNDED_FUNC_CMD", "EXTENDED FUNCTION COMMAND"), + OSP_RESERVED_25(37, "RESERVED_25", "OSP RESERVED 37"), + OSP_RESERVED_26(38, "RESERVED_26", "OSP RESERVED 38"), + OSP_DENY_RESPONSE(39, "DENY_RESPONSE", "DENY RESPONSE"), + OSP_GROUP_AFFILIATION_RESPONSE(40, "GRP_AFFIL_RESP", "GROUP AFFILIATION RESPONSE"), + OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT(41, "SCCB_CCH_BCST_EX", "SECONDARY CONTROL CHANNEL BROADCAST EXPLICIT"), + OSP_GROUP_AFFILIATION_QUERY(42, "GRP_AFFIL_QUERY", "GROUP AFFILIATION QUERY"), + OSP_LOCATION_REGISTRATION_RESPONSE(43, "LOCN_REG_RESPONS", "LOCATION REGISTRATION RESPONSE"), + OSP_UNIT_REGISTRATION_RESPONSE(44, "UNIT_REG_RESPONS", "UNIT REGISTRATION RESPONSE"), + OSP_UNIT_REGISTRATION_COMMAND(45, "UNIT_REG_COMMAND", "UNIT REGISTRATION COMMAND"), + OSP_AUTHENTICATION_COMMAND(46, "AUTH_COMMAND", "AUTHENTICATION COMMAND"), + OSP_UNIT_DEREGISTRATION_ACKNOWLEDGE(47, "DE_REGIST_ACK", "DE-REGISTRATION ACKNOWLEDGE"), + OSP_TDMA_SYNC_BROADCAST(48, "TDMA_SYNC_BCST", "TDMA SYNCHRONIZATION BROADCAST"), + OSP_AUTHENTICATION_DEMAND(49, "AUTH_DEMAND", "AUTHENTICATION DEMAND"), + OSP_AUTHENTICATION_FNE_RESPONSE(50, "AUTH_FNE_RESP", "AUTHENTICATION RESPONSE"), + OSP_IDENTIFIER_UPDATE_TDMA(51, "IDEN_UPDATE_TDMA", "IDENTIFIER UPDATE TDMA"), + OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS(52, "IDEN_UPDATE_VUHF", "IDENTIFIER UPDATE VHF-UHF"), + OSP_TIME_DATE_ANNOUNCEMENT(53, "TIME_DATE_ANNOUN", "TIME AND DATE ANNOUNCEMENT"), + OSP_ROAMING_ADDRESS_COMMAND(54, "ROAM_ADDR_CMD", "ROAMING ADDRESS COMMAND"), + OSP_ROAMING_ADDRESS_UPDATE(55, "ROAM_ADDR_UPDATE", "ROAMING ADDRESS UPDATE"), + OSP_SYSTEM_SERVICE_BROADCAST(56, "SYS_SVC_BCAST", "SYSTEM SERVICE BROADCAST"), + OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST(57, "SEC_CCH_BROADCST", "SECONDARY CONTROL CHANNEL BROADCAST"), + OSP_RFSS_STATUS_BROADCAST(58, "RFSS_STATUS_BCST", "RFSS STATUS BROADCAST"), + OSP_NETWORK_STATUS_BROADCAST(59, "NET_STATUS_BCAST", "NETWORK STATUS BROADCAST"), + OSP_ADJACENT_STATUS_BROADCAST(60, "ADJ SITE STATUS", "ADJACENT SITE STATUS"), + OSP_IDENTIFIER_UPDATE(61, "IDEN_UPDATE", "IDENTIFIER UPDATE"), + OSP_PROTECTION_PARAMETER_BROADCAST(62, "ENCRYPT_PAR_BCST", "ENCRYPTION PARAMENTERS BROADCAST"), + OSP_PROTECTION_PARAMETER_UPDATE(63, "ENCRYPT_PAR_UPDT", "ENCRYPTION PARAMETERS UPDATE"), + OSP_UNKNOWN(-1, "OSP UNKNOWN", "OSP UNKNOWN OPCODE"), + + //Vendor: standard, Inbound Service Packet (ISP) + ISP_GROUP_VOICE_SERVICE_REQUEST(0, "GRP_V_REQ", "GROUP VOICE SERVICE REQUEST"), + ISP_RESERVED_01(1, "RESERVED_01", "ISP RESERVED 1"), + ISP_RESERVED_02(2, "RESERVED_02", "ISP RESERVED 2"), + ISP_RESERVED_03(3, "RESERVED_03", "ISP RESERVED 3"), + ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST(4, "UU_V_REQ", "UNIT-2-UNIT VOICE SERVICE REQUEST"), + ISP_UNIT_TO_UNIT_ANSWER_RESPONSE(5, "UU_ANS_RSP", "UNIT-2-UNIT ANSWER RESPONSE"), + ISP_RESERVED_06(6, "RESERVED_06", "ISP RESERVED 6"), + ISP_RESERVED_07(7, "RESERVED_07", "ISP RESERVED 7"), + ISP_TELEPHONE_INTERCONNECT_EXPLICIT_DIAL_REQUEST(8, "TELE_INT_DIAL_REQ", "TELEPHONE INTERCONNECT EXPLICIT DIAL REQUEST"), + ISP_TELEPHONE_INTERCONNECT_PSTN_REQUEST(9, "TELE_INT_PSTN_REQ", "TELEPHONE INTERCONNECT PSTN REQUEST"), + ISP_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE(10, "TELE_INT_ANS_RSP", "TELEPHONE INTERCONNECT ANSWER RESPONSE"), + ISP_RESERVED_0B(11, "RESERVED_0B", "ISP RESERVED 11"), + ISP_RESERVED_0C(12, "RESERVED_0C", "ISP RESERVED 12"), + ISP_RESERVED_0D(13, "RESERVED_0D", "ISP RESERVED 13"), + ISP_RESERVED_0E(14, "RESERVED_0E", "ISP RESERVED 14"), + ISP_RESERVED_0F(15, "RESERVED_0F", "ISP RESERVED 15"), + ISP_INDIVIDUAL_DATA_SERVICE_REQUEST(16, "IND_DATA_REQ", "INDIVIDUAL DATA SERVICE REQUEST-OBSOLETE"), //OBSOLETE + ISP_GROUP_DATA_SERVICE_REQUEST(17, "GRP_DATA_REQ", "GROUP DATA SERVICE REQUEST-OBSOLETE"), //OBSOLETE + ISP_SNDCP_DATA_CHANNEL_REQUEST(18, "SN-DATA_CHN_REQ", "SNDCP DATA CHANNEL REQUEST"), + ISP_SNDCP_DATA_PAGE_RESPONSE(19, "SN-DATA_PAGE_RES", "SNDCP DATA PAGE REQUEST"), + ISP_SNDCP_RECONNECT_REQUEST(20, "SN-REC_REQ", "SNDCP RECONNECT REQUEST"), + ISP_RESERVED_15(21, "RESERVED_15", "ISP RESERVED 21"), + ISP_RESERVED_16(22, "RESERVED_16", "ISP RESERVED 22"), + ISP_RESERVED_17(23, "RESERVED_17", "ISP RESERVED 23"), + ISP_STATUS_UPDATE_REQUEST(24, "STS_UPDT_REQ", "STATUS UPDATE REQUEST"), + ISP_STATUS_QUERY_RESPONSE(25, "STS_Q_RSP", "STATUS QUERY RESPONSE"), + ISP_STATUS_QUERY_REQUEST(26, "STS_Q_REQ", "STATUS QUERY REQUEST"), + ISP_RESERVED_1B(27, "RESERVED_1B", "ISP RESERVED 27"), + ISP_MESSAGE_UPDATE_REQUEST(28, "MSG_UPDT_REQ", "MESSAGE UPDATE REQUEST"), + ISP_RADIO_UNIT_MONITOR_REQUEST(29, "RAD_MON_REQ", "RADIO UNIT MONITOR REQUEST"), + ISP_RESERVED_1E(30, "RESERVED_1E", "ISP RESERVED 30"), + ISP_CALL_ALERT_REQUEST(31, "CALL_ALRT_REQ", "CALL ALERT REQUEST"), + ISP_UNIT_ACKNOWLEDGE_RESPONSE(32, "ACK_RSP_U", "UNIT ACKNOWLEDGE RESPONSE"), + ISP_RESERVED_21(33, "RESERVED_21", "ISP RESERVED 33"), + ISP_RESERVED_22(34, "RESERVED_22", "ISP RESERVED 34"), + ISP_CANCEL_SERVICE_REQUEST(35, "CAN_SRV_REQ", "CANCEL SERVICE REQUEST"), + ISP_EXTENDED_FUNCTION_RESPONSE(36, "EXT_FNCT_RSP", "EXTENDED FUNCTION RESPONSE"), + ISP_RESERVED_25(37, "RESERVED_25", "ISP RESERVED 37"), + ISP_RESERVED_26(38, "RESERVED_26", "ISP RESERVED 38"), + ISP_EMERGENCY_ALARM_REQUEST(39, "EMRG_ALRM_REQ", "EMERGENCY ALARM REQUEST"), + ISP_GROUP_AFFILIATION_REQUEST(40, "GRP_AFF_REQ", "GROUP AFFILIATION REQUEST"), + ISP_GROUP_AFFILIATION_QUERY_RESPONSE(41, "GRP_AFF_Q_RSP", "GROUP AFFILIATION QUERY RESPONSE"), + ISP_RESERVED_2A(42, "RESERVED_2A", "ISP RESERVED 42"), + ISP_UNIT_DE_REGISTRATION_REQUEST(43, "U_DE_REG_REQ", "UNIT DE-REGISTRATION REQUEST"), + ISP_UNIT_REGISTRATION_REQUEST(44, "U_REG_REQ", "UNIT REGISTRATION REQUEST"), + ISP_LOCATION_REGISTRATION_REQUEST(45, "LOC_REG_REQ", "LOCATION REGISTRATION REQUEST"), + ISP_AUTHENTICATION_QUERY_OBSOLETE(46, "AUTH_Q", "AUTHENTICATION QUERY-OBSOLETE"), //OBSOLETE + ISP_AUTHENTICATION_RESPONSE_OBSOLETE(47, "AUTH_RSP", "AUTHENTICATION RESPONSE-OBSOLETE"), //OBSOLETE + ISP_PROTECTION_PARAMETER_REQUEST(48, "P_PARM_REQ", "ENCRYPTION PARAMETERS REQUEST"), + ISP_RESERVED_31(49, "RESERVED_31", "ISP RESERVED 49"), + ISP_IDENTIFIER_UPDATE_REQUEST(50, "IDEN_UP_REQ", "IDENTIFIER UPDATE REQUEST"), + ISP_RESERVED_33(51, "RESERVED_33", "ISP RESERVED 51"), + ISP_RESERVED_34(52, "RESERVED_34", "ISP RESERVED 52"), + ISP_RESERVED_35(53, "RESERVED_35", "ISP RESERVED 53"), + ISP_ROAMING_ADDRESS_REQUEST(54, "ROAM_ADDR_REQ", "ROAMING ADDRESS REQUEST"), + ISP_ROAMING_ADDRESS_RESPONSE(55, "ROAM_ADDR_RSP", "ROAMING ADDRESS RESPONSE"), + ISP_AUTHENTICATION_RESPONSE(56, "AUTH_RESP", "AUTHENTICATION RESPONSE"), + ISP_AUTHENTICATION_RESPONSE_MUTUAL(57, "AUTH_RESP_M", "AUTHENTICATION RESPONSE MUTUAL"), + ISP_AUTHENTICATION_FNE_RESULT(58, "AUTH_FNE_RST", "AUTHENTICATION RESULT"), + ISP_AUTHENTICATION_SU_DEMAND(59, "AUTH_SU_DMD", "AUTHENTICATION SU DEMAND"), + ISP_RESERVED_3C(60, "RESERVED_3C", "ISP RESERVED 60"), + ISP_RESERVED_3D(61, "RESERVED_3D", "ISP RESERVED 61"), + ISP_RESERVED_3E(62, "RESERVED_3E", "ISP RESERVED 62"), + ISP_RESERVED_3F(63, "RESERVED_3F", "ISP RESERVED 63"), + ISP_UNKNOWN(-1, "ISP UNKNOWN", "ISP UNKNOWN OPCODE"), + + //Vendor: motorola, Inbound Service Packet (ISP) + MOTOROLA_ISP_UNKNOWN(-1, "MOTOROLA ISP UNKNOWN OPCODE", "MOTOROLA ISP UNKNOWN OPCODE"), + + //Vendor: motorola, Outbound Service Packet (OSP) + MOTOROLA_OSP_PATCH_GROUP_ADD(0, "PATCH GROUP ADD", "MOTOROLA PATCH GROUP ADD"), + MOTOROLA_OSP_PATCH_GROUP_DELETE(1, "PATCH GROUP DELE", "MOTOROLA PATCH GROUP DELETE"), + MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT(2, "PTCH GRP VCHN GR", "MOTOROLA PATCH GROUP CHANNEL GRANT"), + MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE(3, "PTCH GRP VCH UPD", "MOTOROLA PATCH GROUP CHANNEL GRANT UPDATE"), + MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "CHANNEL CWID", "CHANNEL CWID"), + MOTOROLA_OSP_SYSTEM_LOADING(9, "SYSTEM LOADING", "SYSTEM LOADING"), + MOTOROLA_OSP_CONTROL_CHANNEL_ID(11, "CCH BASE STAT ID", "CONTROL CHANNEL BASE STATION ID"), + MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN(14, "CCH PLND SHUTDWN", "CONTROL CHANNEL PLANNED SHUTDOWN"), + MOTOROLA_OSP_UNKNOWN(-1, "MOTOROLA OSP UNKNOWN OPCODE", "MOTOROLA OSP UNKNOWN OPCODE"), + + //Vendor: unknown, Inbound Service Packet (ISP) + UNKNOWN_VENDOR_ISP(-1, "UNKNOWN VENDOR/OPCODE ISP", "UNKNOWN VENDOR ISP OPCODE"), + + //Vendor: unknown, Outbound Service Packet (OSP) + UNKNOWN_VENDOR_OSP(-1, "UNKNOWN VENDOR/OPCODE OSP", "UNKNOWN VENDOR OSP OPCODE"); + + private int mCode; + private String mLabel; + private String mDescription; + + public static final EnumSet STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT, + OSP_PROTECTION_PARAMETER_UPDATE); + public static final EnumSet STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST, + ISP_RESERVED_3F); + + Opcode(int code, String label, String description) + { + mCode = code; + mLabel = label; + mDescription = description; + } + + /** + * Formatted, fixed-length label + */ + public String getLabel() + { + return mLabel; + } + + /** + * Full description text + */ + public String getDescription() + { + return mDescription; + } + + /** + * Numeric value for the opcode + */ + public int getCode() + { + return mCode; + } + + @Override + public String toString() + { + return mLabel; + } + + @Deprecated //use the ISP/OSP version of this method instead + public static Opcode fromValue(int value) + { + if(0 <= value && value <= 63) + { + return values()[value]; + } + + return OSP_UNKNOWN; + } + + public static Opcode fromValue(int value, Direction direction, Vendor vendor) + { + switch(vendor) + { + case STANDARD: + if(direction == Direction.OUTBOUND) + { + if(0 <= value && value <= 63) + { + for(Opcode outboundOpcode : STANDARD_OUTBOUND_OPCODES) + { + if(outboundOpcode.getCode() == value) + { + return outboundOpcode; + } + } + } + + return OSP_UNKNOWN; + } + else + { + if(0 <= value && value <= 63) + { + for(Opcode inboundOpcode : STANDARD_INBOUND_OPCODES) + { + if(inboundOpcode.getCode() == value) + { + return inboundOpcode; + } + } + } + + return ISP_UNKNOWN; + } + case MOTOROLA: + if(direction == Direction.INBOUND) + { + return MOTOROLA_ISP_UNKNOWN; + } + else + { + switch(value) + { + case 0x00: + return MOTOROLA_OSP_PATCH_GROUP_ADD; + case 0x01: + return MOTOROLA_OSP_PATCH_GROUP_DELETE; + case 0x02: + return MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT; + case 0x03: + return MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE; + case 0x05: + return MOTOROLA_OSP_TRAFFIC_CHANNEL_ID; + case 0x09: + return MOTOROLA_OSP_SYSTEM_LOADING; + case 0x0B: + return MOTOROLA_OSP_CONTROL_CHANNEL_ID; + case 0x0E: + return MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN; + default: + return MOTOROLA_OSP_UNKNOWN; + } + } + default: + if(direction == Direction.INBOUND) + { + return UNKNOWN_VENDOR_ISP; + } + else + { + return UNKNOWN_VENDOR_OSP; + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ServiceMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ServiceMessage.java deleted file mode 100644 index 9e46a463..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/ServiceMessage.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class ServiceMessage extends TSBKMessage -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 80; - public static final int ENCRYPTED_CHANNEL_FLAG = 81; - public static final int DUPLEX_MODE = 82; - public static final int SESSION_MODE = 83; - - public ServiceMessage( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - public boolean isEmergency() - { - return mMessage.get( EMERGENCY_FLAG ); - } - - public boolean isEncryptedChannel() - { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); - } - - public P25Message.DuplexMode getDuplexMode() - { - return mMessage.get( DUPLEX_MODE ) ? P25Message.DuplexMode.FULL : P25Message.DuplexMode.HALF; - } - - public P25Message.SessionMode getSessionMode() - { - return mMessage.get( SESSION_MODE ) ? - P25Message.SessionMode.CIRCUIT : P25Message.SessionMode.PACKET; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessage.java index 6753a1e0..7f5bf752 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessage.java @@ -1,147 +1,147 @@ package io.github.dsheirer.module.decode.p25.message.tsbk; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.edac.CRC; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.CRCP25; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.P25Utils; import io.github.dsheirer.module.decode.p25.message.P25Message; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.MotorolaOpcode; -import io.github.dsheirer.module.decode.p25.message.tsbk.vendor.VendorOpcode; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; +import io.github.dsheirer.module.decode.p25.reference.Direction; import io.github.dsheirer.module.decode.p25.reference.Vendor; -public class TSBKMessage extends P25Message +import java.util.List; + +/** + * APCO 25 Trunking Signalling Block (TSBK) + */ +public abstract class TSBKMessage extends P25Message { - public static final int MESSAGE_START = 64; - public static final int LAST_BLOCK_FLAG = 64; - public static final int ENCRYPTED_FLAG = 65; - public static final int[] OPCODE = { 66,67,68,69,70,71 }; - public static final int[] VENDOR_ID = { 72,73,74,75,76,77,78,79 }; - - public static final int[] BLOCK1 = { 64,65,66,67,68,69,70,71 }; - public static final int[] BLOCK2 = { 72,73,74,75,76,77,78,79 }; - public static final int[] BLOCK3 = { 80,81,82,83,84,85,86,87 }; - public static final int[] BLOCK4 = { 88,89,90,91,92,93,94,95 }; - public static final int[] BLOCK5 = { 96,97,98,99,100,101,102,103 }; - public static final int[] BLOCK6 = { 104,105,106,107,108,109,110,111 }; - public static final int[] BLOCK7 = { 112,113,114,115,116,117,118,119 }; - public static final int[] BLOCK8 = { 120,121,122,123,124,125,126,127 }; - public static final int[] BLOCK9 = { 128,129,130,131,132,133,134,135 }; - public static final int[] BLOCK10 = { 136,137,138,139,140,141,142,143 }; - public static final int[] BLOCK11 = { 144,145,146,147,148,149,150,151 }; - public static final int[] BLOCK12 = { 152,153,154,155,156,157,158,159 }; - public static final int CRC_START = 144; + private static final int LAST_BLOCK_FLAG = 0; + private static final int ENCRYPTION_FLAG = 1; + private static final int[] OPCODE = {2, 3, 4, 5, 6, 7}; + private static final int[] VENDOR = {8, 9, 10, 11, 12, 13, 14, 15}; - public TSBKMessage( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + private DataUnitID mDataUnitID; + + /** + * Constructs a TSBK from the binary message sequence. + * + * @param dataUnitID TSBK1/2/3 + * @param message binary sequence + * @param nac decoded from the NID + * @param timestamp for the message + */ + public TSBKMessage(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timestamp) { - super( message, duid, aliasList ); + super(message, nac, timestamp); + mDataUnitID = dataUnitID; - /* Since CRC is checked before construction, mark it as passed */ - mCRC = new CRC[ 1 ]; - mCRC[ 0 ] = CRC.PASSED; + //The CRC-CCITT can correct up to 1 bit error or detect 2 or more errors. We mark the message as + //invalid if the algorithm detects more than 1 correctable error. + int errors = CRCP25.correctCCITT80(message, 0, 80); + if(errors > 1) + { + setValid(false); + } } - - public boolean isLastBlock() - { - return mMessage.get( LAST_BLOCK_FLAG ); - } - - public boolean isEncrypted() - { - return mMessage.get( ENCRYPTED_FLAG ); - } - - public Vendor getVendor() - { - return Vendor.fromValue( mMessage.getInt( VENDOR_ID ) ); - } - - public Opcode getOpcode() - { - return Opcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - public MotorolaOpcode getMotorolaOpcode() - { - return MotorolaOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - public VendorOpcode getVendorOpcode() - { - return VendorOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - @Override - public String getMessage() + + @Override + public DataUnitID getDUID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " - " + getMessageHex() ); - - return sb.toString(); + return mDataUnitID; } - - protected String getMessageStub() - { - StringBuilder sb = new StringBuilder(); - Vendor vendor = getVendor(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); + /** + * Indicates if this is an encrypted LCW + */ + public boolean isEncrypted() + { + return getMessage().get(ENCRYPTION_FLAG); + } - if( vendor == Vendor.STANDARD ) - { - if( isEncrypted() ) - { - sb.append( " ENCRYPTED" ); - } - else - { - sb.append( " " ); - sb.append( getOpcode().getLabel() ); - } - } - else - { - sb.append( " " ); - sb.append( vendor.getLabel() ); + /** + * Indicates if this is the last TSBK in a sequence (1-3 blocks) + */ + public boolean isLastBlock() + { + return isLastBlock(getMessage()); + } - if( isEncrypted() ) - { - sb.append( " ENCRYPTED" ); - } - else - { - sb.append( " OPCD:" ); - sb.append( mMessage.getHex( OPCODE, 2 ) ); - } - } - - return sb.toString(); - } - - public String getMessageHex() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mMessage.getHex( BLOCK1, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK2, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK3, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK4, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK5, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK6, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK7, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK8, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK9, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK10, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK11, 2 ) + " " ); - sb.append( mMessage.getHex( BLOCK12, 2 ) + " " ); - - return sb.toString(); - } + /** + * Indicates if this is the last TSBK in a sequence (1-3 blocks) + */ + public static boolean isLastBlock(BinaryMessage binaryMessage) + { + return binaryMessage.get(LAST_BLOCK_FLAG); + } + + /** + * Vendor format for this TSBK. + */ + public Vendor getVendor() + { + return getVendor(getMessage()); + } + + /** + * Lookup the Vendor format for the specified LCW + */ + public static Vendor getVendor(BinaryMessage binaryMessage) + { + return Vendor.fromValue(binaryMessage.getInt(VENDOR)); + } + + /** + * Opcode for this TSBK + */ + public Opcode getOpcode() + { + return getOpcode(getMessage(), getDirection(), getVendor()); + } + + /** + * Opcode for this TSBK + */ + public static Opcode getOpcode(BinaryMessage binaryMessage, Direction direction, Vendor vendor) + { + return Opcode.fromValue(binaryMessage.getInt(OPCODE), direction, vendor); + } + + /** + * Direction - inbound (ISP) or outbound (OSP) + */ + public abstract Direction getDirection(); + + /** + * List of identifiers provided by the message + */ + public abstract List getIdentifiers(); + + /** + * Creates a string with the basic TSBK information + */ + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + sb.append(super.getMessageStub()); + + if(isValid()) + { + sb.append(" ").append(getOpcode().getLabel()); + + P25Utils.pad(sb, 30); + + if(isEncrypted()) + { + sb.append(" ENCRYPTED"); + } + } + else + { + sb.append("**CRC-FAILED**"); + } + + return sb.toString(); + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java index f5b225aa..b2e0503a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -1,202 +1,305 @@ package io.github.dsheirer.module.decode.p25.message.tsbk; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.MotorolaOpcode; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupAdd; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupDelete; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.PlannedControlChannnelShutdown; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.SystemLoading; -import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.TrafficChannelBaseStationIdentification; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.CallAlert; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.DenyResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.IdentifierUpdateTDMA; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.MessageUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.QueuedResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.StatusQuery; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.StatusUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SyncBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.trellis.ViterbiDecoder_1_2_P25; +import io.github.dsheirer.module.decode.p25.P25Interleave; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.isp.UnknownMotorolaISPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.ChannelCWID; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.ChannelLoading; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupAdd; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupDelete; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PlannedChannelShutdown; +import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.UnknownMotorolaOSPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.AuthenticationQuery; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.CallAlertRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.CancelServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.EmergencyAlarmRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.ExtendedFunctionResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.FrequencyBandUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.GroupAffiliationQueryResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.GroupAffiliationRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.GroupVoiceServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.IndividualDataServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.LocationRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.MessageUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.ProtectionParameterRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.RadioUnitMonitorRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.RoamingAddressRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.RoamingAddressResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.SNDCPDataChannelRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.SNDCPDataPageResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.SNDCPReconnectRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.StatusQueryRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.StatusQueryResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.StatusUpdateRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.TelephoneInterconnectAnswerResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.TelephoneInterconnectPstnRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitAcknowledgeResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitDeRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitRegistrationRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitToUnitVoiceServiceAnswerResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnitToUnitVoiceServiceRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.UnknownISPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.AcknowledgeResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.AdjacentStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.AuthenticationCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.CallAlert; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.DenyResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.ExtendedFunctionCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.FrequencyBandUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.FrequencyBandUpdateTDMA; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.FrequencyBandUpdateVUHF; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupAffiliationQuery; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupAffiliationResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupDataChannelAnnouncement; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupDataChannelAnnouncementExplicit; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.GroupVoiceChannelGrantUpdateExplicit; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.IndividualDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.LocationRegistrationResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.MessageUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.NetworkStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.ProtectionParameterUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.QueuedResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.RFSSStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.RadioUnitMonitorCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.RoamingAddressCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SNDCPDataChannelAnnouncementExplicit; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SNDCPDataChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SNDCPDataPageRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SecondaryControlChannelBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.StatusQuery; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.StatusUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SyncBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SystemServiceBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.TelephoneInterconnectAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.TelephoneInterconnectVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.TelephoneInterconnectVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitDeRegistrationAcknowledge; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitRegistrationCommand; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitRegistrationResponse; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitToUnitAnswerRequest; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitToUnitVoiceChannelGrant; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnitToUnitVoiceChannelGrantUpdate; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.UnknownOSPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.unknown.isp.UnknownVendorISPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.unknown.osp.UnknownVendorOSPMessage; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; +import io.github.dsheirer.module.decode.p25.reference.Direction; import io.github.dsheirer.module.decode.p25.reference.Vendor; +/** + * Factory for creating Trunking Signalling Block (TSBK) message parser classes + */ public class TSBKMessageFactory { - public static TSBKMessage getMessage( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - Vendor vendor = Vendor.fromValue( - message.getInt( TSBKMessage.VENDOR_ID ) ); - - switch( vendor ) - { - case STANDARD: - Opcode opcode = - Opcode.fromValue( message.getInt( TSBKMessage.OPCODE ) ); + private static final ViterbiDecoder_1_2_P25 VITERBI_HALF_RATE_DECODER = new ViterbiDecoder_1_2_P25(); - switch( opcode ) - { - case ACKNOWLEDGE_RESPONSE: - return new AcknowledgeResponse( message, duid, aliasList ); - case ADJACENT_STATUS_BROADCAST: - return new AdjacentStatusBroadcast( message, duid, aliasList ); - case AUTHENTICATION_COMMAND: - return new AuthenticationCommand( message, duid, aliasList ); - case CALL_ALERT: - return new CallAlert( message, duid, aliasList ); - case DENY_RESPONSE: - return new DenyResponse( message, duid, aliasList ); - case EXTENDED_FUNCTION_COMMAND: - return new ExtendedFunctionCommand( message, duid, aliasList ); - case GROUP_AFFILIATION_QUERY: - return new GroupAffiliationQuery( message, duid, aliasList ); - case GROUP_AFFILIATION_RESPONSE: - return new GroupAffiliationResponse( message, duid, aliasList ); - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - return new GroupDataChannelAnnouncement( message, duid, aliasList ); - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - return new GroupDataChannelAnnouncementExplicit( message, duid, aliasList ); - case GROUP_DATA_CHANNEL_GRANT: - return new GroupDataChannelGrant( message, duid, aliasList ); - case GROUP_VOICE_CHANNEL_GRANT: - return new GroupVoiceChannelGrant( message, duid, aliasList ); - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - return new GroupVoiceChannelGrantUpdate( message, duid, aliasList ); - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - return new GroupVoiceChannelGrantUpdateExplicit( message, duid, aliasList ); - case IDENTIFIER_UPDATE_NON_VUHF: - return new IdentifierUpdateNonVUHF( message, duid, aliasList ); - case IDENTIFIER_UPDATE_TDMA: - return new IdentifierUpdateTDMA( message, duid, aliasList ); - case IDENTIFIER_UPDATE_VHF_UHF_BANDS: - return new IdentifierUpdateVUHF( message, duid, aliasList ); - case INDIVIDUAL_DATA_CHANNEL_GRANT: - return new IndividualDataChannelGrant( message, duid, aliasList ); - case LOCATION_REGISTRATION_RESPONSE: - return new LocationRegistrationResponse( message, duid, aliasList ); - case MESSAGE_UPDATE: - return new MessageUpdate( message, duid, aliasList ); - case NETWORK_STATUS_BROADCAST: - return new NetworkStatusBroadcast( message, duid, aliasList ); - case QUEUED_RESPONSE: - return new QueuedResponse( message, duid, aliasList ); - case PROTECTION_PARAMETER_UPDATE: - return new ProtectionParameterUpdate( message, duid, aliasList ); - case RADIO_UNIT_MONITOR_COMMAND: - return new RadioUnitMonitorCommand( message, duid, aliasList ); - case RFSS_STATUS_BROADCAST: - return new RFSSStatusBroadcast( message, duid, aliasList ); - case ROAMING_ADDRESS_COMMAND: - return new RoamingAddressCommand( message, duid, aliasList ); - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - return new SecondaryControlChannelBroadcast( message, duid, aliasList ); - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - return new SecondaryControlChannelBroadcastExplicit( message, duid, aliasList ); - case SNDCP_DATA_CHANNEL_GRANT: - return new SNDCPDataChannelGrant( message, duid, aliasList ); - case SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - return new SNDCPDataChannelAnnouncementExplicit( message, duid, aliasList ); - case SNDCP_DATA_PAGE_REQUEST: - return new SNDCPDataPageRequest( message, duid, aliasList ); - case STATUS_QUERY: - return new StatusQuery( message, duid, aliasList ); - case STATUS_UPDATE: - return new StatusUpdate( message, duid, aliasList ); - case TDMA_SYNC_BROADCAST: - return new SyncBroadcast( message, duid, aliasList ); - case SYSTEM_SERVICE_BROADCAST: - return new SystemServiceBroadcast( message, duid, aliasList ); - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - return new TelephoneInterconnectAnswerRequest( message, duid, aliasList ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - return new TelephoneInterconnectVoiceChannelGrant( message, duid, aliasList ); - case TIME_DATE_ANNOUNCEMENT: - return new TimeAndDateAnnouncement( message, duid, aliasList ); - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - return new UnitDeregistrationAcknowledge( message, duid, aliasList ); - case UNIT_REGISTRATION_COMMAND: - return new UnitRegistrationCommand( message, duid, aliasList ); - case UNIT_REGISTRATION_RESPONSE: - return new UnitRegistrationResponse( message, duid, aliasList ); - case UNIT_TO_UNIT_ANSWER_REQUEST: - return new UnitToUnitAnswerRequest( message, duid, aliasList ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - return new UnitToUnitVoiceChannelGrant( message, duid, aliasList ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - return new UnitToUnitVoiceChannelGrantUpdate( message, duid, aliasList ); - default: - return new TSBKMessage( message, duid, aliasList ); - } - - case MOTOROLA: - MotorolaOpcode motorolaOpcode = MotorolaOpcode. - fromValue( message.getInt( TSBKMessage.OPCODE ) ); - - switch( motorolaOpcode ) - { - case CCH_PLANNED_SHUTDOWN: - return new PlannedControlChannnelShutdown( message, duid, aliasList ); - case CONTROL_CHANNEL_ID: - return new ControlChannelBaseStationIdentification( message, duid, aliasList ); - case PATCH_GROUP_ADD: - return new PatchGroupAdd( message, duid, aliasList ); - case PATCH_GROUP_DELETE: - return new PatchGroupDelete( message, duid, aliasList ); - case PATCH_GROUP_CHANNEL_GRANT: - return new PatchGroupVoiceChannelGrant( message, duid, aliasList ); - case PATCH_GROUP_CHANNEL_GRANT_UPDATE: - return new PatchGroupVoiceChannelGrantUpdate( message, duid, aliasList ); - case SYSTEM_LOAD: - return new SystemLoading( message, duid, aliasList ); - case TRAFFIC_CHANNEL_ID: - return new TrafficChannelBaseStationIdentification( message, duid, aliasList ); - default: - } - - return new MotorolaTSBKMessage( message, duid, aliasList ); - default: - return new TSBKMessage( message, duid, aliasList ); - } - } -} \ No newline at end of file + public static TSBKMessage create(Direction direction, DataUnitID dataUnitID, + CorrectedBinaryMessage correctedBinaryMessage, int nac, long timestamp) + { + //Get deinterleaved header chunk + CorrectedBinaryMessage deinterleaved = P25Interleave.deinterleaveChunk(P25Interleave.DATA_DEINTERLEAVE, correctedBinaryMessage); + + //Decode 1/2 rate trellis encoded PDU header + CorrectedBinaryMessage message = VITERBI_HALF_RATE_DECODER.decode(deinterleaved); + + if(message == null) + { + return null; + } + + Vendor vendor = TSBKMessage.getVendor(message); + Opcode opcode = TSBKMessage.getOpcode(message, direction, vendor); + + switch(opcode) + { + case ISP_AUTHENTICATION_QUERY_OBSOLETE: + return new AuthenticationQuery(dataUnitID, message, nac, timestamp); + case ISP_CALL_ALERT_REQUEST: + return new CallAlertRequest(dataUnitID, message, nac, timestamp); + case ISP_CANCEL_SERVICE_REQUEST: + return new CancelServiceRequest(dataUnitID, message, nac, timestamp); + case ISP_EMERGENCY_ALARM_REQUEST: + return new EmergencyAlarmRequest(dataUnitID, message, nac, timestamp); + case ISP_EXTENDED_FUNCTION_RESPONSE: + return new ExtendedFunctionResponse(dataUnitID, message, nac, timestamp); + case ISP_GROUP_AFFILIATION_QUERY_RESPONSE: + return new GroupAffiliationQueryResponse(dataUnitID, message, nac, timestamp); + case ISP_GROUP_AFFILIATION_REQUEST: + return new GroupAffiliationRequest(dataUnitID, message, nac, timestamp); + case ISP_GROUP_VOICE_SERVICE_REQUEST: + return new GroupVoiceServiceRequest(dataUnitID, message, nac, timestamp); + case ISP_IDENTIFIER_UPDATE_REQUEST: + return new FrequencyBandUpdateRequest(dataUnitID, message, nac, timestamp); + case ISP_INDIVIDUAL_DATA_SERVICE_REQUEST: + return new IndividualDataServiceRequest(dataUnitID, message, nac, timestamp); + case ISP_LOCATION_REGISTRATION_REQUEST: + return new LocationRegistrationRequest(dataUnitID, message, nac, timestamp); + case ISP_MESSAGE_UPDATE_REQUEST: + return new MessageUpdateRequest(dataUnitID, message, nac, timestamp); + case ISP_PROTECTION_PARAMETER_REQUEST: + return new ProtectionParameterRequest(dataUnitID, message, nac, timestamp); + case ISP_RADIO_UNIT_MONITOR_REQUEST: + return new RadioUnitMonitorRequest(dataUnitID, message, nac, timestamp); + case ISP_ROAMING_ADDRESS_REQUEST: + return new RoamingAddressRequest(dataUnitID, message, nac, timestamp); + case ISP_ROAMING_ADDRESS_RESPONSE: + return new RoamingAddressResponse(dataUnitID, message, nac, timestamp); + case ISP_SNDCP_DATA_CHANNEL_REQUEST: + return new SNDCPDataChannelRequest(dataUnitID, message, nac, timestamp); + case ISP_SNDCP_DATA_PAGE_RESPONSE: + return new SNDCPDataPageResponse(dataUnitID, message, nac, timestamp); + case ISP_SNDCP_RECONNECT_REQUEST: + return new SNDCPReconnectRequest(dataUnitID, message, nac, timestamp); + case ISP_STATUS_QUERY_REQUEST: + return new StatusQueryRequest(dataUnitID, message, nac, timestamp); + case ISP_STATUS_QUERY_RESPONSE: + return new StatusQueryResponse(dataUnitID, message, nac, timestamp); + case ISP_STATUS_UPDATE_REQUEST: + return new StatusUpdateRequest(dataUnitID, message, nac, timestamp); + case ISP_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE: + return new TelephoneInterconnectAnswerResponse(dataUnitID, message, nac, timestamp); + case ISP_TELEPHONE_INTERCONNECT_PSTN_REQUEST: + return new TelephoneInterconnectPstnRequest(dataUnitID, message, nac, timestamp); + case ISP_UNIT_ACKNOWLEDGE_RESPONSE: + return new UnitAcknowledgeResponse(dataUnitID, message, nac, timestamp); + case ISP_UNIT_REGISTRATION_REQUEST: + return new UnitRegistrationRequest(dataUnitID, message, nac, timestamp); + case ISP_UNIT_DE_REGISTRATION_REQUEST: + return new UnitDeRegistrationRequest(dataUnitID, message, nac, timestamp); + case ISP_UNIT_TO_UNIT_ANSWER_RESPONSE: + return new UnitToUnitVoiceServiceAnswerResponse(dataUnitID, message, nac, timestamp); + case ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST: + return new UnitToUnitVoiceServiceRequest(dataUnitID, message, nac, timestamp); + case OSP_ACKNOWLEDGE_RESPONSE: + return new AcknowledgeResponse(dataUnitID, message, nac, timestamp); + case OSP_ADJACENT_STATUS_BROADCAST: + return new AdjacentStatusBroadcast(dataUnitID, message, nac, timestamp); + case OSP_AUTHENTICATION_COMMAND: + return new AuthenticationCommand(dataUnitID, message, nac, timestamp); + case OSP_CALL_ALERT: + return new CallAlert(dataUnitID, message, nac, timestamp); + case OSP_DENY_RESPONSE: + return new DenyResponse(dataUnitID, message, nac, timestamp); + case OSP_EXTENDED_FUNCTION_COMMAND: + return new ExtendedFunctionCommand(dataUnitID, message, nac, timestamp); + case OSP_GROUP_AFFILIATION_QUERY: + return new GroupAffiliationQuery(dataUnitID, message, nac, timestamp); + case OSP_GROUP_AFFILIATION_RESPONSE: + return new GroupAffiliationResponse(dataUnitID, message, nac, timestamp); + case OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT: + return new GroupDataChannelAnnouncement(dataUnitID, message, nac, timestamp); + case OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + return new GroupDataChannelAnnouncementExplicit(dataUnitID, message, nac, timestamp); + case OSP_GROUP_DATA_CHANNEL_GRANT: + return new GroupDataChannelGrant(dataUnitID, message, nac, timestamp); + case OSP_GROUP_VOICE_CHANNEL_GRANT: + return new GroupVoiceChannelGrant(dataUnitID, message, nac, timestamp); + case OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE: + return new GroupVoiceChannelGrantUpdate(dataUnitID, message, nac, timestamp); + case OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + return new GroupVoiceChannelGrantUpdateExplicit(dataUnitID, message, nac, timestamp); + case OSP_IDENTIFIER_UPDATE: + return new FrequencyBandUpdate(dataUnitID, message, nac, timestamp); + case OSP_IDENTIFIER_UPDATE_TDMA: + return new FrequencyBandUpdateTDMA(dataUnitID, message, nac, timestamp); + case OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS: + return new FrequencyBandUpdateVUHF(dataUnitID, message, nac, timestamp); + case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT: + return new IndividualDataChannelGrant(dataUnitID, message, nac, timestamp); + case OSP_LOCATION_REGISTRATION_RESPONSE: + return new LocationRegistrationResponse(dataUnitID, message, nac, timestamp); + case OSP_MESSAGE_UPDATE: + return new MessageUpdate(dataUnitID, message, nac, timestamp); + case OSP_NETWORK_STATUS_BROADCAST: + return new NetworkStatusBroadcast(dataUnitID, message, nac, timestamp); + case OSP_PROTECTION_PARAMETER_UPDATE: + return new ProtectionParameterUpdate(dataUnitID, message, nac, timestamp); + case OSP_RADIO_UNIT_MONITOR_COMMAND: + return new RadioUnitMonitorCommand(dataUnitID, message, nac, timestamp); + case OSP_QUEUED_RESPONSE: + return new QueuedResponse(dataUnitID, message, nac, timestamp); + case OSP_ROAMING_ADDRESS_COMMAND: + return new RoamingAddressCommand(dataUnitID, message, nac, timestamp); + case OSP_RFSS_STATUS_BROADCAST: + return new RFSSStatusBroadcast(dataUnitID, message, nac, timestamp); + case OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST: + return new SecondaryControlChannelBroadcast(dataUnitID, message, nac, timestamp); + case OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + return new SNDCPDataChannelAnnouncementExplicit(dataUnitID, message, nac, timestamp); + case OSP_SNDCP_DATA_CHANNEL_GRANT: + return new SNDCPDataChannelGrant(dataUnitID, message, nac, timestamp); + case OSP_SNDCP_DATA_PAGE_REQUEST: + return new SNDCPDataPageRequest(dataUnitID, message, nac, timestamp); + case OSP_STATUS_QUERY: + return new StatusQuery(dataUnitID, message, nac, timestamp); + case OSP_STATUS_UPDATE: + return new StatusUpdate(dataUnitID, message, nac, timestamp); + case OSP_TDMA_SYNC_BROADCAST: + return new SyncBroadcast(dataUnitID, message, nac, timestamp); + case OSP_SYSTEM_SERVICE_BROADCAST: + return new SystemServiceBroadcast(dataUnitID, message, nac, timestamp); + case OSP_TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + return new TelephoneInterconnectAnswerRequest(dataUnitID, message, nac, timestamp); + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + return new TelephoneInterconnectVoiceChannelGrant(dataUnitID, message, nac, timestamp); + case OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + return new TelephoneInterconnectVoiceChannelGrantUpdate(dataUnitID, message, nac, timestamp); + case OSP_UNIT_DEREGISTRATION_ACKNOWLEDGE: + return new UnitDeRegistrationAcknowledge(dataUnitID, message, nac, timestamp); + case OSP_UNIT_REGISTRATION_COMMAND: + return new UnitRegistrationCommand(dataUnitID, message, nac, timestamp); + case OSP_UNIT_REGISTRATION_RESPONSE: + return new UnitRegistrationResponse(dataUnitID, message, nac, timestamp); + case OSP_UNIT_TO_UNIT_ANSWER_REQUEST: + return new UnitToUnitAnswerRequest(dataUnitID, message, nac, timestamp); + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + return new UnitToUnitVoiceChannelGrant(dataUnitID, message, nac, timestamp); + case OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + return new UnitToUnitVoiceChannelGrantUpdate(dataUnitID, message, nac, timestamp); + + case MOTOROLA_ISP_UNKNOWN: + return new UnknownMotorolaISPMessage(dataUnitID, message, nac, timestamp); + + case MOTOROLA_OSP_CONTROL_CHANNEL_ID: + return new ChannelCWID(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN: + return new PlannedChannelShutdown(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_TRAFFIC_CHANNEL_ID: + return new ChannelCWID(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_PATCH_GROUP_ADD: + return new PatchGroupAdd(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_PATCH_GROUP_DELETE: + return new PatchGroupDelete(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT: + return new PatchGroupVoiceChannelGrant(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE: + return new PatchGroupVoiceChannelGrantUpdate(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_SYSTEM_LOADING: + return new ChannelLoading(dataUnitID, message, nac, timestamp); + case MOTOROLA_OSP_UNKNOWN: + return new UnknownMotorolaOSPMessage(dataUnitID, message, nac, timestamp); + + case UNKNOWN_VENDOR_ISP: + return new UnknownVendorISPMessage(dataUnitID, message, nac, timestamp); + + case UNKNOWN_VENDOR_OSP: + return new UnknownVendorOSPMessage(dataUnitID, message, nac, timestamp); + + default: + if(direction == Direction.INBOUND) + { + return new UnknownISPMessage(dataUnitID, message, nac, timestamp); + } + else + { + return new UnknownOSPMessage(dataUnitID, message, nac, timestamp); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/UnitChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/UnitChannelGrant.java deleted file mode 100644 index 61208f0d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/UnitChannelGrant.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class UnitChannelGrant extends ChannelGrant -{ - public static final int[] TARGET_ADDRESS = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] SOURCE_ADDRESS = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, - 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - public UnitChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" FROM:"); - sb.append(getSourceAddress()); - - sb.append(" TO:"); - sb.append(getTargetAddress()); - - return sb.toString(); - } - - public String getTargetAddress() - { - return mMessage.getHex(TARGET_ADDRESS, 6); - } - - public String getSourceAddress() - { - return mMessage.getHex(SOURCE_ADDRESS, 6); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - @Override - public Alias getFromIDAlias() - { - if(mAliasList != null) - { - return mAliasList.getTalkgroupAlias(getFromID()); - } - - return null; - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java deleted file mode 100644 index f7e38182..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class ControlChannelBaseStationIdentification extends MotorolaTSBKMessage implements IdentifierReceiver -{ - public static final int[] CHARACTER_1 = {80, 81, 82, 83, 84, 85}; - public static final int[] CHARACTER_2 = {86, 87, 88, 89, 90, 91}; - public static final int[] CHARACTER_3 = {92, 93, 94, 95, 96, 97}; - public static final int[] CHARACTER_4 = {98, 99, 100, 101, 102, 103}; - public static final int[] CHARACTER_5 = {104, 105, 106, 107, 108, 109}; - public static final int[] CHARACTER_6 = {110, 111, 112, 113, 114, 115}; - public static final int[] CHARACTER_7 = {116, 117, 118, 119, 120, 121}; - public static final int[] CHARACTER_8 = {122, 123, 124, 125, 126, 127}; - - public static final int[] IDENTIFIER = {128, 129, 130, 131}; - public static final int[] CHANNEL = {132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate; - - public ControlChannelBaseStationIdentification(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.CONTROL_CHANNEL_ID.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" CHAN:" + getIdentifier() + "-" + getChannel()); - - sb.append(" CWID:" + getCWID()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" UP:" + getUplinkFrequency()); - - return sb.toString(); - } - - - public String getCWID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getCharacter(CHARACTER_1)); - sb.append(getCharacter(CHARACTER_2)); - sb.append(getCharacter(CHARACTER_3)); - sb.append(getCharacter(CHARACTER_4)); - sb.append(getCharacter(CHARACTER_5)); - sb.append(getCharacter(CHARACTER_6)); - sb.append(getCharacter(CHARACTER_7)); - sb.append(getCharacter(CHARACTER_8)); - - return sb.toString(); - } - - private String getCharacter(int[] field) - { - int value = mMessage.getInt(field); - - if(value != 0) - { - return String.valueOf((char)(value + 43)); - } - - return null; - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public int getChannel() - { - return mMessage.getInt(CHANNEL); - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - mIdentifierUpdate.getUplinkFrequency(getChannel()); - } - - return 0; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - mIdentifierUpdate.getDownlinkFrequency(getChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - - identifiers[0] = getIdentifier(); - - return identifiers; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java deleted file mode 100644 index 2e734ffb..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -public enum MotorolaOpcode -{ - PATCH_GROUP_ADD( "ADD PATCH GROUP", "ADD PATCH SUPER GROUP", 0x00 ), - PATCH_GROUP_DELETE( "DELETE PATCH GROUP", "DELETE PATCH SUPER GROUP", 0x01 ), - PATCH_GROUP_CHANNEL_GRANT( "PATCH VOICE CHANNEL GRANT", "PATCH VOICE CHANNEL GRANT", 0x02 ), - PATCH_GROUP_CHANNEL_GRANT_UPDATE( "PATCH VOICE CHANNEL UPDATE", "PATCH VOICE CHANNEL GRANT UPDATE", 0x03 ), - OP04( "MOT OP04:UNKNOWN", "Opcode 0x04 Unknown", 0x04 ), - TRAFFIC_CHANNEL_ID( "TRAFFIC CHAN STATION ID", "TRAFFIC CHAN STATION ID", 0x05 ), - OP06( "MOT OP06:UNKNOWN ", "Opcode 0x06 Unknown", 0x06 ), - OP07( "MOT OP07:UNKNOWN ", "Opcode 0x07 Unknown", 0x07 ), - OP08( "MOT OP08:UNKNOWN ", "Opcode 0x08 Unknown", 0x08 ), - SYSTEM_LOAD( "SYSTEM LOADING", "SYSTEM LOAD", 0x09 ), - OP0A( "OP0A:UNKNOWN ", "Opcode 0x0A Unknown", 0x0A ), - CONTROL_CHANNEL_ID( "CONTROL CHAN BASE STATION ID", "CONTROL CHAN BASE STATION ID", 0x0B ), - OP0C( "MOT OP0C:UNKNOWN ", "Opcode 0x0C Unknown", 0x0C ), - OP0D( "MOT OP0D:UNKNOWN ", "Opcode 0x0D Unknown", 0x0D ), - CCH_PLANNED_SHUTDOWN( "PLANNED CONTROL CHANNEL SHUTDOWN ", "PLANNED CONTROL CHANNEL SHUTDOWN", 0x0E ), - OP0F( "MOT OP0F:UNKNOWN ", "Opcode 0x0F Unknown", 0x0F ), - OP10( "MOT OP10:UNKNOWN ", "Opcode 0x10 Unknown", 0x10 ), - OP11( "MOT OP11:UNKNOWN ", "Opcode 0x11 Unknown", 0x11 ), - OP12( "MOT OP12:UNKNOWN ", "Opcode 0x12 Unknown", 0x12 ), - OP13( "MOT OP13:UNKNOWN ", "Opcode 0x13 Unknown", 0x13 ), - OP14( "MOT OP14:UNKNOWN ", "Opcode 0x14 Unknown", 0x14 ), - OP15( "MOT OP15:UNKNOWN ", "Opcode 0x15 Unknown", 0x15 ), - OP16( "MOT OP16:UNKNOWN ", "Opcode 0x16 Unknown", 0x16 ), - OP17( "MOT OP17:UNKNOWN ", "Opcode 0x17 Unknown", 0x17 ), - OP18( "MOT OP18:UNKNOWN ", "Opcode 0x18 Unknown", 0x18 ), - OP19( "MOT OP19:UNKNOWN ", "Opcode 0x19 Unknown", 0x19 ), - OP1A( "MOT OP1A:UNKNOWN ", "Opcode 0x1A Unknown", 0x1A ), - OP1B( "MOT OP1B:UNKNOWN ", "Opcode 0x1B Unknown", 0x1B ), - OP1C( "MOT OP1C:UNKNOWN ", "Opcode 0x1C Unknown", 0x1C ), - OP1D( "MOT OP1D:UNKNOWN ", "Opcode 0x1D Unknown", 0x1D ), - OP1E( "MOT OP1E:UNKNOWN ", "Opcode 0x1E Unknown", 0x1E ), - OP1F( "MOT OP1F:UNKNOWN ", "Opcode 0x1F Unknown", 0x1F ), - OP20( "MOT OP20:UNKNOWN ", "Opcode 0x20 Unknown", 0x20 ), - OP21( "MOT OP21:UNKNOWN ", "Opcode 0x21 Unknown", 0x21 ), - OP22( "MOT OP22:UNKNOWN ", "Opcode 0x22 Unknown", 0x22 ), - OP23( "MOT OP23:UNKNOWN ", "Opcode 0x23 Unknown", 0x23 ), - OP24( "MOT OP24:UNKNOWN ", "Opcode 0x24 Unknown", 0x24 ), - OP25( "MOT OP25:UNKNOWN ", "Opcode 0x25 Unknown", 0x25 ), - OP26( "MOT OP26:UNKNOWN ", "Opcode 0x26 Unknown", 0x26 ), - OP27( "MOT OP27:UNKNOWN ", "Opcode 0x27 Unknown", 0x27 ), - OP28( "MOT OP28:UNKNOWN ", "Opcode 0x28 Unknown", 0x28 ), - OP29( "MOT OP29:UNKNOWN ", "Opcode 0x29 Unknown", 0x29 ), - OP2A( "MOT OP2A:UNKNOWN ", "Opcode 0x2A Unknown", 0x2A ), - OP2B( "MOT OP2B:UNKNOWN ", "Opcode 0x2B Unknown", 0x2B ), - OP2C( "MOT OP2C:UNKNOWN ", "Opcode 0x2C Unknown", 0x2C ), - OP2D( "MOT OP2D:UNKNOWN ", "Opcode 0x2D Unknown", 0x2D ), - OP2E( "MOT OP2E:UNKNOWN ", "Opcode 0x2E Unknown", 0x2E ), - OP2F( "MOT OP2F:UNKNOWN ", "Opcode 0x2F Unknown", 0x2F ), - OP30( "MOT OP30:UNKNOWN ", "Opcode 0x30 Unknown", 0x30 ), - OP31( "MOT OP31:UNKNOWN ", "Opcode 0x31 Unknown", 0x31 ), - OP32( "MOT OP32:UNKNOWN ", "Opcode 0x32 Unknown", 0x32 ), - OP33( "MOT OP33:UNKNOWN ", "Opcode 0x33 Unknown", 0x33 ), - OP34( "MOT OP34:UNKNOWN ", "Opcode 0x34 Unknown", 0x34 ), - OP35( "MOT OP35:UNKNOWN ", "Opcode 0x35 Unknown", 0x35 ), - OP36( "MOT OP36:UNKNOWN ", "Opcode 0x36 Unknown", 0x36 ), - OP37( "MOT OP37:UNKNOWN ", "Opcode 0x37 Unknown", 0x37 ), - OP38( "MOT OP38:UNKNOWN ", "Opcode 0x38 Unknown", 0x38 ), - OP39( "MOT OP39:UNKNOWN ", "Opcode 0x39 Unknown", 0x39 ), - OP3A( "MOT OP3A:UNKNOWN ", "Opcode 0x3A Unknown", 0x3A ), - OP3B( "MOT OP3B:UNKNOWN ", "Opcode 0x3B Unknown", 0x3B ), - OP3C( "MOT OP3C:UNKNOWN ", "Opcode 0x3C Unknown", 0x3C ), - OP3D( "MOT OP3D:UNKNOWN ", "Opcode 0x3D Unknown", 0x3D ), - OP3E( "MOT OP3E:UNKNOWN ", "Opcode 0x3E Unknown", 0x3E ), - OP3F( "MOT OP3F:UNKNOWN ", "Opcode 0x3F Unknown", 0x3F ), - UNKNOWN( "UNKNOWN OPCODE ", "Unknown", -1 ); - - private String mLabel; - private String mDescription; - private int mCode; - - private MotorolaOpcode( String label, String description, int code ) - { - mLabel = label; - mDescription = description; - mCode = code; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } - - public String getDescription() - { - return mDescription; - } - - public int getCode() - { - return mCode; - } - - public static MotorolaOpcode fromValue( int value ) - { - if( 0 <= value && value <= 0x3F ) - { - return MotorolaOpcode.values()[ value ]; - } - - return UNKNOWN; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java deleted file mode 100644 index a85713ac..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class MotorolaTSBKMessage extends TSBKMessage -{ - public MotorolaTSBKMessage( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - protected String getMessageStub() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); /* NAC is the system id for TSBK messages */ - sb.append( " " ); - sb.append( getDUID().getLabel() ); - - sb.append( " MOTOROLA " ); - - if( isEncrypted() ) - { - sb.append( " ENCRYPTED " ); - } - - sb.append( getMotorolaOpcode().getLabel() ); - - return sb.toString(); - } - - public MotorolaOpcode getMotorolaOpcode() - { - return MotorolaOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroup.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroup.java deleted file mode 100644 index b7a859a9..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroup.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -import java.util.ArrayList; -import java.util.List; - -public abstract class PatchGroup extends MotorolaTSBKMessage -{ - public static final int[] PATCH_GROUP_ADDRESS = { 80,81,82,83,84,85,86,87, - 88,89,90,91,92,93,94,95 }; - public static final int[] GROUP_ADDRESS_1 = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111 }; - public static final int[] GROUP_ADDRESS_2 = { 112,113,114,115,116,117,118, - 119,120,121,122,123,124,125,126,127 }; - public static final int[] GROUP_ADDRESS_3 = { 128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143 }; - - private List mPatchedTalkgroups; - - public PatchGroup( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " " ); - sb.append( getPatchGroupAddress() ); - sb.append(" "); - sb.append(getPatchedTalkgroups()); - - return sb.toString(); - } - - public String getPatchGroupAddress() - { - return mMessage.getHex( PATCH_GROUP_ADDRESS, 4 ); - } - - /** - * List of de-deplicated patched talkgroups contained in this message - */ - public List getPatchedTalkgroups() - { - if(mPatchedTalkgroups == null) - { - mPatchedTalkgroups = new ArrayList<>(); - - mPatchedTalkgroups.add(getGroupAddress1()); - - String group2 = getGroupAddress2(); - - if(!mPatchedTalkgroups.contains(group2)) - { - mPatchedTalkgroups.add(group2); - } - - String group3 = getGroupAddress3(); - - if(!mPatchedTalkgroups.contains(group3)) - { - mPatchedTalkgroups.add(group3); - } - } - - return mPatchedTalkgroups; - } - - public String getGroupAddress1() - { - return mMessage.getHex( GROUP_ADDRESS_1, 4 ); - } - - public String getGroupAddress2() - { - return mMessage.getHex( GROUP_ADDRESS_2, 4 ); - } - - public String getGroupAddress3() - { - return mMessage.getHex( GROUP_ADDRESS_3, 4 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java deleted file mode 100644 index 10244c95..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class PatchGroupAdd extends PatchGroup -{ - public PatchGroupAdd( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.PATCH_GROUP_ADD.getLabel(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java deleted file mode 100644 index f3ddbfa1..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class PatchGroupDelete extends PatchGroup -{ - public PatchGroupDelete( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.PATCH_GROUP_DELETE.getLabel(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java deleted file mode 100644 index 5d93b82f..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java +++ /dev/null @@ -1,187 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class PatchGroupVoiceChannelGrant extends MotorolaTSBKMessage implements IdentifierReceiver -{ - /* Service Options */ - public static final int EMERGENCY_FLAG = 80; - public static final int ENCRYPTED_CHANNEL_FLAG = 81; - public static final int DUPLEX_MODE = 82; - public static final int SESSION_MODE = 83; - - public static final int[] PRIORITY = {85, 86, 87}; - public static final int[] IDENTIFIER = {88, 89, 90, 91}; - public static final int[] CHANNEL = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; - public static final int[] PATCH_GROUP_ADDRESS = {104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119}; - public static final int[] SOURCE_ADDRESS = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, - 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate; - - public PatchGroupVoiceChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - public boolean isTDMAChannel() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.isTDMA(); - } - - return false; - } - - @Override - public String getEventType() - { - return MotorolaOpcode.PATCH_GROUP_CHANNEL_GRANT.getLabel(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" PATCH GROUP:"); - sb.append(getPatchGroupAddress()); - sb.append(" FROM:"); - sb.append(getSourceAddress()); - sb.append(" PRI:"); - sb.append(getPriority()); - - sb.append(" CHAN:"); - sb.append(getChannelIdentifier() + "-" + getChannelNumber()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" UP:" + getUplinkFrequency()); - - sb.append(" SESSION MODE:"); - sb.append(getSessionMode().name()); - - return sb.toString(); - } - - public String getPatchGroupAddress() - { - return mMessage.getHex(PATCH_GROUP_ADDRESS, 4); - } - - public String getSourceAddress() - { - return mMessage.getHex(SOURCE_ADDRESS, 6); - } - - public boolean isEmergency() - { - return mMessage.get(EMERGENCY_FLAG); - } - - public boolean isEncryptedChannel() - { - return mMessage.get(ENCRYPTED_CHANNEL_FLAG); - } - - public DuplexMode getDuplexMode() - { - return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; - } - - public SessionMode getSessionMode() - { - return mMessage.get(SESSION_MODE) ? - SessionMode.CIRCUIT : SessionMode.PACKET; - } - - /** - * 1 = Lowest, 4 = Default, 7 = Highest - */ - public int getPriority() - { - return mMessage.getInt(PRIORITY); - } - - public int getChannelIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public int getChannelNumber() - { - return mMessage.getInt(CHANNEL); - } - - public String getChannel() - { - return getChannelIdentifier() + "-" + getChannelNumber(); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - mIdentifierUpdate = message; - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[1]; - identifiers[0] = getChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); - } - - return 0; - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java deleted file mode 100644 index 98b3b3f2..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java +++ /dev/null @@ -1,197 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class PatchGroupVoiceChannelGrantUpdate extends MotorolaTSBKMessage implements IdentifierReceiver -{ - public static final int[] IDENTIFIER_1 = {80, 81, 82, 83}; - public static final int[] CHANNEL_1 = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; - public static final int[] PATCH_GROUP_ADDRESS_1 = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] IDENTIFIER_2 = {112, 113, 114, 115}; - public static final int[] CHANNEL_2 = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; - public static final int[] PATCH_GROUP_ADDRESS_2 = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate1; - private IBandIdentifier mIdentifierUpdate2; - - public PatchGroupVoiceChannelGrantUpdate(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.PATCH_GROUP_CHANNEL_GRANT_UPDATE.getLabel(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - - sb.append(" PATCH GRP1:"); - sb.append(getPatchGroupAddress1()); - sb.append(" GRP2:"); - sb.append(getPatchGroupAddress2()); - - sb.append(" CHAN 1:"); - sb.append(getChannel1()); - sb.append(" DN:" + getDownlinkFrequency1()); - - sb.append(" CHAN 2:"); - sb.append(getChannel2()); - sb.append(" DN:" + getDownlinkFrequency2()); - - return sb.toString(); - } - - public String getPatchGroupAddress1() - { - return mMessage.getHex(PATCH_GROUP_ADDRESS_1, 4); - } - - public String getPatchGroupAddress2() - { - return mMessage.getHex(PATCH_GROUP_ADDRESS_2, 4); - } - - public int getChannelIdentifier1() - { - return mMessage.getInt(IDENTIFIER_1); - } - - public int getChannelNumber1() - { - return mMessage.getInt(CHANNEL_1); - } - - public String getChannel1() - { - return getChannelIdentifier1() + "-" + getChannelNumber1(); - } - - public int getChannelIdentifier2() - { - return mMessage.getInt(IDENTIFIER_2); - } - - public int getChannelNumber2() - { - return mMessage.getInt(CHANNEL_2); - } - - public String getChannel2() - { - return getChannelIdentifier2() + "-" + getChannelNumber2(); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getChannelIdentifier1()) - { - mIdentifierUpdate1 = message; - } - - if(identifier == getChannelIdentifier2()) - { - mIdentifierUpdate2 = message; - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - identifiers[0] = getChannelIdentifier1(); - identifiers[1] = getChannelIdentifier2(); - - return identifiers; - } - - public long getDownlinkFrequency1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.getDownlinkFrequency(getChannelNumber1()); - } - - return 0; - } - - public boolean isTDMAChannel1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.isTDMA(); - } - - return false; - } - - public long getUplinkFrequency1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.getUplinkFrequency(getChannelNumber1()); - } - - return 0; - } - - public long getDownlinkFrequency2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.getDownlinkFrequency(getChannelNumber2()); - } - - return 0; - } - - public boolean isTDMAChannel2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.isTDMA(); - } - - return false; - } - - public long getUplinkFrequency2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.getUplinkFrequency(getChannelNumber2()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java deleted file mode 100644 index 1654165d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class PlannedControlChannnelShutdown extends MotorolaTSBKMessage -{ - public PlannedControlChannnelShutdown( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.CCH_PLANNED_SHUTDOWN.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - return sb.toString(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/SystemLoading.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/SystemLoading.java deleted file mode 100644 index 7e11128c..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/SystemLoading.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class SystemLoading extends MotorolaTSBKMessage -{ - public SystemLoading( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.SYSTEM_LOAD.getLabel(); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " - " + getMessageHex() ); - - return sb.toString(); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java deleted file mode 100644 index c833cb88..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.motorola; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class TrafficChannelBaseStationIdentification extends MotorolaTSBKMessage -{ - public static final int[] CHARACTER_1 = { 80,81,82,83,84,85 }; - public static final int[] CHARACTER_2 = { 86,87,88,89,90,91 }; - public static final int[] CHARACTER_3 = { 92,93,94,95,96,97 }; - public static final int[] CHARACTER_4 = { 98,99,100,101,102,103 }; - public static final int[] CHARACTER_5 = { 104,105,106,107,108,109 }; - public static final int[] CHARACTER_6 = { 110,111,112,113,114,115 }; - public static final int[] CHARACTER_7 = { 116,117,118,119,120,121 }; - public static final int[] CHARACTER_8 = { 122,123,124,125,126,127 }; - - public TrafficChannelBaseStationIdentification( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return MotorolaOpcode.TRAFFIC_CHANNEL_ID.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " ID:" + getID() ); - - return sb.toString(); - } - - - public String getID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getCharacter( CHARACTER_1 ) ); - sb.append( getCharacter( CHARACTER_2 ) ); - sb.append( getCharacter( CHARACTER_3 ) ); - sb.append( getCharacter( CHARACTER_4 ) ); - sb.append( getCharacter( CHARACTER_5 ) ); - sb.append( getCharacter( CHARACTER_6 ) ); - sb.append( getCharacter( CHARACTER_7 ) ); - sb.append( getCharacter( CHARACTER_8 ) ); - - return sb.toString(); - } - - private String getCharacter( int[] field ) - { - int value = mMessage.getInt( field ); - - return String.valueOf( (char)( value + 43 ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/isp/UnknownMotorolaISPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/isp/UnknownMotorolaISPMessage.java new file mode 100644 index 00000000..bf30b1ab --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/isp/UnknownMotorolaISPMessage.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown/Unrecognized opcode message. + */ +public class UnknownMotorolaISPMessage extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnknownMotorolaISPMessage(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MOTOROLA **UNRECOGNIZED ISP OPCODE**"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelCWID.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelCWID.java new file mode 100644 index 00000000..577d9b33 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelCWID.java @@ -0,0 +1,137 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ChannelCWID extends OSPMessage implements IFrequencyBandReceiver +{ + public static final int[] CHARACTER_1 = {16, 17, 18, 19, 20, 21}; + public static final int[] CHARACTER_2 = {22, 23, 24, 25, 26, 27}; + public static final int[] CHARACTER_3 = {28, 29, 30, 31, 32, 33}; + public static final int[] CHARACTER_4 = {34, 35, 36, 37, 38, 39}; + public static final int[] CHARACTER_5 = {40, 41, 42, 43, 44, 45}; + public static final int[] CHARACTER_6 = {46, 47, 48, 49, 50, 51}; + public static final int[] CHARACTER_7 = {52, 53, 54, 55, 56, 57}; + public static final int[] CHARACTER_8 = {58, 59, 60, 61, 62, 63}; + public static final int[] FREQUENCY_BAND = {64, 65, 66, 67}; + public static final int[] CHANNEL_NUMBER = {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private String mCWID; + private IChannelDescriptor mChannel; + + public ChannelCWID(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitID, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + if(hasChannel()) + { + sb.append(" CHAN:" + getChannel()); + sb.append(" CWID:" + getCWID()); + } + else + { + sb.append(" CWID NOT SPECIFIED"); + } + + return sb.toString(); + } + + public boolean hasChannel() + { + return getMessage().getInt(CHANNEL_NUMBER) != 0; + } + + + public String getCWID() + { + if(mCWID == null) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getCharacter(CHARACTER_1)); + sb.append(getCharacter(CHARACTER_2)); + sb.append(getCharacter(CHARACTER_3)); + sb.append(getCharacter(CHARACTER_4)); + sb.append(getCharacter(CHARACTER_5)); + sb.append(getCharacter(CHARACTER_6)); + sb.append(getCharacter(CHARACTER_7)); + sb.append(getCharacter(CHARACTER_8)); + + mCWID = sb.toString(); + } + + return mCWID; + } + + private String getCharacter(int[] field) + { + int value = getMessage().getInt(field); + + if(value != 0) + { + return String.valueOf((char)(value + 43)); + } + + return ""; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), + getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelLoading.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelLoading.java new file mode 100644 index 00000000..3c7a0cc8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/ChannelLoading.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Channel loading + */ +public class ChannelLoading extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public ChannelLoading(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MOTOROLA"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupAdd.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupAdd.java new file mode 100644 index 00000000..2970b9a9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupAdd.java @@ -0,0 +1,179 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +public class PatchGroupAdd extends OSPMessage +{ + public static final int[] PATCH_GROUP_ADDRESS = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + public static final int[] GROUP_ADDRESS_1 = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + public static final int[] GROUP_ADDRESS_2 = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + public static final int[] GROUP_ADDRESS_3 = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private APCO25PatchGroup mPatchGroup; + private TalkgroupIdentifier mGroupAddress1; + private TalkgroupIdentifier mGroupAddress2; + private TalkgroupIdentifier mGroupAddress3; + private List mPatchedTalkgroups; + private List mIdentifiers; + + public PatchGroupAdd(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timeslot) + { + super(dataUnitID, message, nac, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" PATCH GROUP:").append(getPatchGroup()); + return sb.toString(); + } + + public Identifier getPatchGroup() + { + if(mPatchGroup == null) + { + PatchGroup patchGroup = new PatchGroup(APCO25ToTalkgroup.createGroup(getPatchAddress())); + patchGroup.addPatchedGroups(getPatchedTalkgroups()); + mPatchGroup = APCO25PatchGroup.create(patchGroup); + } + + return mPatchGroup; + } + + private int getPatchAddress() + { + return getMessage().getInt(PATCH_GROUP_ADDRESS); + } + + + /** + * List of de-duplicated patched talkgroups contained in this message + */ + public List getPatchedTalkgroups() + { + if(mPatchedTalkgroups == null) + { + mPatchedTalkgroups = new ArrayList<>(); + + if(hasGroupAddress1()) + { + mPatchedTalkgroups.add(getGroupAddress1()); + } + + if(hasGroupAddress2()) + { + mPatchedTalkgroups.add(getGroupAddress2()); + } + + if(hasGroupAddress3()) + { + mPatchedTalkgroups.add(getGroupAddress3()); + } + } + + return mPatchedTalkgroups; + } + + public TalkgroupIdentifier getGroupAddress1() + { + if(mGroupAddress1 == null) + { + mGroupAddress1 = APCO25ToTalkgroup.createGroup(getAddress1()); + } + + return mGroupAddress1; + } + + public boolean hasGroupAddress1() + { + return getPatchAddress() != getAddress1(); + } + + private int getAddress1() + { + return getMessage().getInt(GROUP_ADDRESS_1); + } + + public TalkgroupIdentifier getGroupAddress2() + { + if(mGroupAddress2 == null) + { + mGroupAddress2 = APCO25ToTalkgroup.createGroup(getAddress2()); + } + + return mGroupAddress2; + } + + private int getAddress2() + { + return getMessage().getInt(GROUP_ADDRESS_2); + } + + public boolean hasGroupAddress2() + { + return getPatchAddress() != getAddress2() && getAddress1() != getAddress2(); + } + + public TalkgroupIdentifier getGroupAddress3() + { + if(mGroupAddress3 == null) + { + mGroupAddress3 = APCO25ToTalkgroup.createGroup(getAddress3()); + } + + return mGroupAddress3; + } + + private int getAddress3() + { + return getMessage().getInt(GROUP_ADDRESS_3); + } + + public boolean hasGroupAddress3() + { + return getPatchAddress() != getAddress3() && getAddress1() != getAddress3() && getAddress2() != getAddress3(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupDelete.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupDelete.java new file mode 100644 index 00000000..91b811a7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupDelete.java @@ -0,0 +1,182 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +public class PatchGroupDelete extends OSPMessage +{ + public static final int[] PATCH_GROUP_ADDRESS = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + public static final int[] GROUP_ADDRESS_1 = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + public static final int[] GROUP_ADDRESS_2 = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + public static final int[] GROUP_ADDRESS_3 = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private APCO25PatchGroup mPatchGroup; + private TalkgroupIdentifier mGroupAddress1; + private TalkgroupIdentifier mGroupAddress2; + private TalkgroupIdentifier mGroupAddress3; + private List mPatchedTalkgroups; + private List mIdentifiers; + + public PatchGroupDelete(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timeslot) + { + super(dataUnitID, message, nac, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" PATCH GROUP:").append(getPatchGroup()); + return sb.toString(); + } + + public Identifier getPatchGroup() + { + if(mPatchGroup == null) + { + PatchGroup patchGroup = new PatchGroup(APCO25ToTalkgroup.createGroup(getPatchAddress())); + patchGroup.addPatchedGroups(getPatchedTalkgroups()); + mPatchGroup = APCO25PatchGroup.create(patchGroup); + } + + return mPatchGroup; + } + + private int getPatchAddress() + { + return getMessage().getInt(PATCH_GROUP_ADDRESS); + } + + + /** + * List of de-duplicated patched talkgroups contained in this message + */ + public List getPatchedTalkgroups() + { + if(mPatchedTalkgroups == null) + { + mPatchedTalkgroups = new ArrayList<>(); + + if(hasAddress1()) + { + mPatchedTalkgroups.add(getGroupAddress1()); + } + + if(hasGroupAddress2()) + { + mPatchedTalkgroups.add(getGroupAddress2()); + } + + if(hasGroupAddress3()) + { + mPatchedTalkgroups.add(getGroupAddress3()); + } + } + + return mPatchedTalkgroups; + } + + public TalkgroupIdentifier getGroupAddress1() + { + if(mGroupAddress1 == null) + { + mGroupAddress1 = APCO25ToTalkgroup.createGroup(getAddress1()); + } + + return mGroupAddress1; + } + + private int getAddress1() + { + return getMessage().getInt(GROUP_ADDRESS_1); + } + + private boolean hasAddress1() + { + return getPatchAddress() != getAddress1(); + } + + public TalkgroupIdentifier getGroupAddress2() + { + if(mGroupAddress2 == null) + { + mGroupAddress2 = APCO25ToTalkgroup.createGroup(getAddress2()); + } + + return mGroupAddress2; + } + + private int getAddress2() + { + return getMessage().getInt(GROUP_ADDRESS_2); + } + + public boolean hasGroupAddress2() + { + return getPatchAddress() != getAddress2() && + getAddress1() != getAddress2(); + } + + public TalkgroupIdentifier getGroupAddress3() + { + if(mGroupAddress3 == null) + { + mGroupAddress3 = APCO25ToTalkgroup.createGroup(getAddress3()); + } + + return mGroupAddress3; + } + + private int getAddress3() + { + return getMessage().getInt(GROUP_ADDRESS_3); + } + + public boolean hasGroupAddress3() + { + return getPatchAddress() != getAddress2() && + getAddress1() != getAddress3() && + getAddress2() != getAddress3(); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrant.java new file mode 100644 index 00000000..d914e5ea --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrant.java @@ -0,0 +1,132 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.patch.PatchGroupIdentifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +public class PatchGroupVoiceChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + public static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + public static final int[] FREQUENCY_BAND = {24, 25, 26, 27}; + public static final int[] CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + public static final int[] PATCH_GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + public static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mSourceAddress; + private PatchGroupIdentifier mPatchGroup; + private List mIdentifiers; + + public PatchGroupVoiceChannelGrant(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timestlot) + { + super(dataUnitID, message, nac, timestlot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + sb.append(" PATCH GROUP:").append(getPatchGroup()); + sb.append(" FROM:").append(getSourceAddress()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + + return sb.toString(); + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public PatchGroupIdentifier getPatchGroup() + { + if(mPatchGroup == null) + { + mPatchGroup = APCO25PatchGroup.create(new PatchGroup(APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCH_GROUP_ADDRESS)))); + } + + return mPatchGroup; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrantUpdate.java new file mode 100644 index 00000000..631883dd --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PatchGroupVoiceChannelGrantUpdate.java @@ -0,0 +1,153 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.patch.PatchGroupIdentifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +public class PatchGroupVoiceChannelGrantUpdate extends OSPMessage implements IFrequencyBandReceiver +{ + public static final int[] FREQUENCY_BAND_1 = {16, 17, 18, 19}; + public static final int[] CHANNEL_NUMBER_1 = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + public static final int[] PATCH_GROUP_1 = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + public static final int[] FREQUENCY_BAND_2 = {48, 49, 50, 51}; + public static final int[] CHANNEL_NUMBER_2 = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + public static final int[] PATCH_GROUP_2 = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private PatchGroupIdentifier mPatchGroup1; + private PatchGroupIdentifier mPatchGroup2; + private APCO25Channel mChannel1; + private APCO25Channel mChannel2; + private List mIdentifiers; + + public PatchGroupVoiceChannelGrantUpdate(DataUnitID dataUnitID, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitID, message, nac, timestamp); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" PATCH GRP1:"); + sb.append(getPatchGroup1()); + sb.append(" CHAN 1:"); + sb.append(getChannel1()); + + if(hasPatchGroup2()) + { + sb.append(" GRP2:"); + sb.append(getPatchGroup2()); + sb.append(" CHAN 2:"); + sb.append(getChannel2()); + } + + return sb.toString(); + } + + public boolean hasPatchGroup2() + { + return (getMessage().getInt(PATCH_GROUP_1) != getMessage().getInt(PATCH_GROUP_2)) && + getMessage().getInt(PATCH_GROUP_2) != 0; + } + + public PatchGroupIdentifier getPatchGroup1() + { + if(mPatchGroup1 == null) + { + mPatchGroup1 = APCO25PatchGroup.create(new PatchGroup(APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCH_GROUP_1)))); + } + + return mPatchGroup1; + } + + public PatchGroupIdentifier getPatchGroup2() + { + if(mPatchGroup2 == null) + { + mPatchGroup2 = APCO25PatchGroup.create(new PatchGroup(APCO25ToTalkgroup.createGroup(getMessage().getInt(PATCH_GROUP_2)))); + } + + return mPatchGroup2; + } + + public APCO25Channel getChannel1() + { + if(mChannel1 == null) + { + mChannel1 = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_1), getMessage().getInt(CHANNEL_NUMBER_1)); + } + + return mChannel1; + } + + public APCO25Channel getChannel2() + { + if(hasPatchGroup2() && mChannel2 == null) + { + mChannel2 = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_2), getMessage().getInt(CHANNEL_NUMBER_2)); + } + + return mChannel2; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel1()); + if(hasPatchGroup2()) + { + channels.add(getChannel2()); + } + return channels; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPatchGroup1()); + if(hasPatchGroup2()) + { + mIdentifiers.add(getPatchGroup2()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PlannedChannelShutdown.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PlannedChannelShutdown.java new file mode 100644 index 00000000..ee0aea2e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/PlannedChannelShutdown.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Planned channel shutdown + */ +public class PlannedChannelShutdown extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public PlannedChannelShutdown(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MOTOROLA"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/UnknownMotorolaOSPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/UnknownMotorolaOSPMessage.java new file mode 100644 index 00000000..0933eac1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/UnknownMotorolaOSPMessage.java @@ -0,0 +1,58 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown/Unrecognized opcode message. + */ +public class UnknownMotorolaOSPMessage extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnknownMotorolaOSPMessage(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MOTOROLA **UNRECOGNIZED OSP OPCODE**"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java deleted file mode 100644 index fd9424b8..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class AcknowledgeResponse extends TSBKMessage -{ - public static final int ADDITIONAL_INFORMATION_FLAG = 80; - public static final int EXTENDED_ADDRESS_FLAG = 81; - public static final int[] SERVICE_TYPE = { 82,83,84,85,86,87 }; - public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, - 101,102,103,104,105,106,107 }; - public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115,116, - 117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public AcknowledgeResponse( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.ACKNOWLEDGE_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SVC TYPE: " + getServiceType() ); - - if( hasAdditionalInformation() ) - { - if( hasExtendedAddress() ) - { - sb.append( " WACN: " + getWACN() ); - sb.append( " SYS ID: " + getSystemID() ); - } - else - { - sb.append( " SRC: " + getSourceAddress() ); - } - } - - return sb.toString(); - } - - public boolean hasAdditionalInformation() - { - return mMessage.get( ADDITIONAL_INFORMATION_FLAG ); - } - - public boolean hasExtendedAddress() - { - return mMessage.get( EXTENDED_ADDRESS_FLAG ); - } - - public String getServiceType() - { - return mMessage.getHex( SERVICE_TYPE, 2 ); - } - - public String getWACN() - { - if( hasAdditionalInformation() && !hasExtendedAddress() ) - { - return mMessage.getHex( WACN, 5 ); - } - - return null; - } - - public String getSystemID() - { - if( hasAdditionalInformation() && !hasExtendedAddress() ) - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - return null; - } - - public String getSourceAddress() - { - if( hasAdditionalInformation() && !hasExtendedAddress() ) - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - return null; - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java deleted file mode 100644 index f2adf300..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IAdjacentSite; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class AdjacentStatusBroadcast extends TSBKMessage implements IdentifierReceiver, IAdjacentSite -{ - public static final int[] LOCATION_REGISTRATION_AREA = {80, 81, 82, 83, 84, 85, 86, 87}; - public static final int CONVENTIONAL_CHANNEL_FLAG = 88; - public static final int SITE_FAILURE_CONDITION_FLAG = 89; - public static final int VALID_FLAG = 90; - public static final int ACTIVE_NETWORK_CONNECTION_FLAG = 91; - public static final int[] SYSTEM_ID = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; - public static final int[] RFSS_ID = {104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SITE_ID = {112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] IDENTIFIER = {120, 121, 122, 123}; - public static final int[] CHANNEL = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SYSTEM_SERVICE_CLASS = {136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate; - - public AdjacentStatusBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return Opcode.ADJACENT_STATUS_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLRA()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); - - sb.append(" CHAN:" + getIdentifier() + "-" + getChannel()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" UP:" + getUplinkFrequency()); - - if(isConventionalChannel()) - { - sb.append(" CONVENTIONAL"); - } - - if(isSiteFailureCondition()) - { - sb.append(" FAILURE-CONDITION"); - } - - if(isValidSiteInformation()) - { - sb.append(" VALID-INFO"); - } - - if(hasActiveNetworkConnection()) - { - sb.append(" ACTIVE-NETWORK-CONN"); - } - - sb.append(getSystemServiceClass()); - - return sb.toString(); - } - - @Override - public String getDownlinkChannel() - { - return getIdentifier() + "-" + getChannel(); - } - - @Override - public String getUplinkChannel() - { - return getIdentifier() + "-" + getChannel(); - } - - public String getLRA() - { - return mMessage.getHex(LOCATION_REGISTRATION_AREA, 2); - } - - public boolean isConventionalChannel() - { - return mMessage.get(CONVENTIONAL_CHANNEL_FLAG); - } - - public boolean isSiteFailureCondition() - { - return mMessage.get(SITE_FAILURE_CONDITION_FLAG); - } - - public boolean isValidSiteInformation() - { - return mMessage.get(VALID_FLAG); - } - - public boolean hasActiveNetworkConnection() - { - return mMessage.get(ACTIVE_NETWORK_CONNECTION_FLAG); - } - - public String getUniqueID() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getSystemID()); - sb.append(":"); - sb.append(getRFSS()); - sb.append(":"); - sb.append(getSiteID()); - - return sb.toString(); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSS() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getChannel() - { - return mMessage.getInt(CHANNEL); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public String getSystemServiceClass() - { - return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannel()); - } - - return 0; - } - - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - /* we're only expecting 1 identifier, so use whatever is received */ - mIdentifierUpdate = message; - } - - public int[] getIdentifiers() - { - int[] idens = new int[1]; - - idens[0] = getIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java deleted file mode 100644 index c782be73..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class AuthenticationCommand extends TSBKMessage -{ - public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, - 101,102,103,104,105,106,107 }; - public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115,116, - 117,118,119 }; - public static final int[] TARGET_ID = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public AuthenticationCommand( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.AUTHENTICATION_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " WACN: " + getWACN() ); - sb.append( " SYS ID: " + getSystemID() ); - sb.append( " TGT ID: " + getTargetID() ); - - return sb.toString(); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getTargetID() - { - return mMessage.getHex( TARGET_ID, 6 ); - } - - public String getFullTargetID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getWACN() ); - sb.append( "-" ); - sb.append( getSystemID() ); - sb.append( "-" ); - sb.append( getTargetID() ); - - return sb.toString(); - } - - @Override - public String getToID() - { - return getTargetID(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/CallAlert.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/CallAlert.java deleted file mode 100644 index 7673d368..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/CallAlert.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class CallAlert extends TSBKMessage -{ - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ID = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public CallAlert( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.CALL_ALERT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SRC ID: " + getSourceID() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - @Override - public String getFromID() - { - return getSourceID(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/DenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/DenyResponse.java deleted file mode 100644 index e1895272..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/DenyResponse.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.DenyReason; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class DenyResponse extends TSBKMessage -{ - public static final int ADDITIONAL_INFORMATION_FLAG = 80; - public static final int[] SERVICE_TYPE = { 82,83,84,85,86,87 }; - public static final int[] REASON_CODE = { 88,89,90,91,92,93,94,95 }; - - public static final int[] CALL_OPTIONS = { 96,97,98,99,100,101,102,103 }; - public static final int[] SOURCE_GROUP = { 104,105,106,107,108,109,110,111 }; - public static final int[] SOURCE_ADDRESS = { 96,97,98,99,100,101,102,103,104, - 105,106,107,108,109,110,111 }; - - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public DenyResponse( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.DENY_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - sb.append( " REASON:" + getReason().name() ); - - return sb.toString(); - } - - public DenyReason getReason() - { - int code = mMessage.getInt( REASON_CODE ); - - return DenyReason.fromCode( code ); - } - - public boolean hasAdditionalInformation() - { - return mMessage.get( ADDITIONAL_INFORMATION_FLAG ); - } - - public Opcode getServiceType() - { - return Opcode.fromValue( mMessage.getInt( SERVICE_TYPE ) ); - } - - public String getSourceAddress() - { - switch( getServiceType() ) - { - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - return mMessage.getHex( SOURCE_GROUP, 4 ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - default: - break; - } - - return null; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java deleted file mode 100644 index ebd70c75..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.ExtendedFunction; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class ExtendedFunctionCommand extends TSBKMessage -{ - public static final int[] EXTENDED_FUNCTION = { 80,81,82,83,84,85,86,87, - 88,89,90,91,92,93,94,95 }; - public static final int[] ARGUMENT = { 96,97,98,99,100,101,102,103,104,105, - 106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public ExtendedFunctionCommand( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.EXTENDED_FUNCTION_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " EXTENDED FUNCTION:" + getExtendedFunction().getLabel() ); - - sb.append( " ARGUMENT: " + getTargetAddress() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public ExtendedFunction getExtendedFunction() - { - return ExtendedFunction.fromValue( mMessage.getInt( EXTENDED_FUNCTION ) ); - } - - public String getArgument() - { - return mMessage.getHex( ARGUMENT, 6 ); - } - - public String getSourceAddress() - { - switch( getExtendedFunction() ) - { - case RADIO_CHECK: - case RADIO_CHECK_ACK: - case RADIO_DETACH: - case RADIO_DETACH_ACK: - case RADIO_INHIBIT: - case RADIO_INHIBIT_ACK: - case RADIO_UNINHIBIT: - case RADIO_UNINHIBIT_ACK: - return getArgument(); - default: - break; - } - - return null; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java deleted file mode 100644 index 72d3e0d9..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupAffiliationQuery extends TSBKMessage -{ - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public GroupAffiliationQuery( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_AFFILIATION_QUERY.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SRC ADDR: " + getSourceAddress() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java deleted file mode 100644 index 596902d2..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.Response; - -public class GroupAffiliationResponse extends TSBKMessage -{ - public static final int LOCAL_GLOBAL_AFFILIATION_FLAG = 80; - public static final int[] AFFILIATION_RESPONSE = { 86,87 }; - public static final int[] ANNOUNCEMENT_GROUP_ADDRESS = { 88,89,90,91, - 92,93,94,95,96,97,98,99,100,101,102,103 }; - public static final int[] GROUP_ADDRESS = { 104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119 }; - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public GroupAffiliationResponse( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_AFFILIATION_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " AFFILIATION:" + getResponse().name() ); - sb.append( " ANNOUNCE GROUP:" + getAnnouncementGroupAddress() ); - sb.append( " GRP ADDR:" + getGroupAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getAffiliationScope() - { - return mMessage.get( LOCAL_GLOBAL_AFFILIATION_FLAG) ? " GLOBAL" : " LOCAL"; - } - - public Response getResponse() - { - int response = mMessage.getInt( AFFILIATION_RESPONSE ); - - return Response.fromValue( response ); - } - - public String getAnnouncementGroupAddress() - { - return mMessage.getHex( ANNOUNCEMENT_GROUP_ADDRESS, 4 ); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - @Override - public String getFromID() - { - return getGroupAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java deleted file mode 100644 index 672e19d0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class IdentifierUpdate extends TSBKMessage implements IBandIdentifier -{ - public static final int[] IDENTIFIER = { 80,81,82,83 }; - - public static final int[] CHANNEL_SPACING = { 102,103,104,105,106,107,108, - 109,110,111 }; - - public static final int[] BASE_FREQUENCY = { 112,113,114,115,116,117,118, - 119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136, - 137,138,139,140,141,142,143 }; - - public IdentifierUpdate( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - @Override - public long getChannelSpacing() - { - return mMessage.getLong( CHANNEL_SPACING ) * 125l; - } - - @Override - public long getBaseFrequency() - { - return mMessage.getLong( BASE_FREQUENCY ) * 5l; - } - - @Override - public abstract long getTransmitOffset(); - - @Override - public long getDownlinkFrequency(int channelNumber) - { - return getBaseFrequency() + ( channelNumber * getChannelSpacing() ); - } - - @Override - public long getUplinkFrequency(int channelNumber) - { - return getDownlinkFrequency(channelNumber) + getTransmitOffset(); - } - - @Override - public boolean isTDMA() - { - return false; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java deleted file mode 100644 index 4a5bd0ce..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class IdentifierUpdateNonVUHF extends IdentifierUpdate -{ - public static final int[] BANDWIDTH = { 84,85,86,87,88,89,90,91,92 }; - - public static final int TRANSMIT_OFFSET_VHF_UHF_SIGN = 93; - - public static final int[] TRANSMIT_OFFSET = { 94,95,96,97,98,99,100,101 }; - - public IdentifierUpdateNonVUHF( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.IDENTIFIER_UPDATE_NON_VUHF.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append(toString()); - - return sb.toString(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( " ID:" + getIdentifier() ); - sb.append( " BASE:" + getBaseFrequency() ); - sb.append( " BANDWIDTH:" + getBandwidth() ); - sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - - return sb.toString(); - } - - /** - * Channel bandwidth in hertz - */ - public int getBandwidth() - { - return mMessage.getInt( BANDWIDTH ) * 125; - } - - /** - * Transmit offset in hertz - */ - @Override - public long getTransmitOffset() - { - long offset = mMessage.getLong( TRANSMIT_OFFSET ) * 250000l; - - if( mMessage.get( TRANSMIT_OFFSET_VHF_UHF_SIGN ) ) - { - return offset; - } - else - { - return -offset; - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java deleted file mode 100644 index 57eaf71e..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java +++ /dev/null @@ -1,186 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class IdentifierUpdateTDMA extends IdentifierUpdate -{ - public static final int[] CHANNEL_TYPE = { 84,85,86,87 }; - public static final int TRANSMIT_OFFSET_SIGN = 88; - public static final int[] TRANSMIT_OFFSET = { 89,90,91,92,93,94,95,96,97,98, - 99,100,101 }; - - public IdentifierUpdateTDMA( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.IDENTIFIER_UPDATE_TDMA.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append(toString()); - - return sb.toString(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( " ID:" + getIdentifier() ); - sb.append( " BASE:" + getBaseFrequency() ); - sb.append( " BANDWIDTH:" + getBandwidth() ); - sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - sb.append( " TYPE:" + getChannelType().toString() ); - - return sb.toString(); - } - - public ChannelType getChannelType() - { - return ChannelType.fromValue( mMessage.getInt( CHANNEL_TYPE) ); - } - - /** - * Channel bandwidth in hertz - */ - public int getBandwidth() - { - return getChannelType().getBandwidth(); - } - - /** - * Transmit offset in hertz - */ - @Override - public long getTransmitOffset() - { - long offset = mMessage.getLong( TRANSMIT_OFFSET ) * - getChannelSpacing(); - - if( mMessage.get( TRANSMIT_OFFSET_SIGN ) ) - { - return offset; - } - else - { - return -offset; - } - } - - @Override - public long getDownlinkFrequency(int channelNumber) - { - if(isTDMA()) - { - return getBaseFrequency() + ( (channelNumber / getChannelType().getSlotsPerCarrier()) * getChannelSpacing() ); - } - else - { - return super.getDownlinkFrequency(channelNumber); - } - } - - @Override - public boolean isTDMA() - { - return getChannelType().getAccessType() == AccessType.TDMA; - } - - public enum ChannelType - { - TYPE_0( AccessType.FDMA, 12500, 1, Vocoder.HALF_RATE ), - TYPE_1( AccessType.FDMA, 12500, 1, Vocoder.FULL_RATE ), - TYPE_2( AccessType.FDMA, 6250, 1, Vocoder.HALF_RATE ), - TYPE_3( AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE ), - TYPE_4( AccessType.TDMA, 25000, 4, Vocoder.HALF_RATE ), - TYPE_5( AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE ), //HD8PSK simulcast - UNKNOWN( AccessType.UNKNOWN, 0, 0, Vocoder.HALF_RATE ); - - private AccessType mAccessType; - private int mBandwidth; - private int mSlotsPerCarrier; - private Vocoder mVocoder; - - private ChannelType( AccessType accessType, - int bandwidth, - int slots, - Vocoder vocoder ) - { - mAccessType = accessType; - mBandwidth = bandwidth; - mSlotsPerCarrier = slots; - mVocoder = vocoder; - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - if( this.equals( TYPE_5 ) ) - { - sb.append("H-D8PSK SIMULCAST " ); - } - - sb.append( mAccessType.name() ); - sb.append( " BANDWIDTH:" + mBandwidth ); - sb.append( " TIMESLOTS PER CARRIER:" + mSlotsPerCarrier ); - sb.append( " VOCODER:" + mVocoder.name() ); - - return sb.toString(); - } - - public static ChannelType fromValue( int value ) - { - if( 0 <= value && value <= 5 ) - { - return ChannelType.values()[ value ]; - } - - return ChannelType.UNKNOWN; - } - - public AccessType getAccessType() - { - return mAccessType; - } - - public int getBandwidth() - { - return mBandwidth; - } - - public int getSlotsPerCarrier() - { - return mSlotsPerCarrier; - } - - public Vocoder getVocoder() - { - return mVocoder; - } - } - - public enum AccessType - { - FDMA,TDMA,UNKNOWN; - } - - public enum Vocoder - { - HALF_RATE, FULL_RATE; - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java deleted file mode 100644 index bf8bb500..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class IdentifierUpdateVUHF extends IdentifierUpdate -{ - public static final int[] BANDWIDTH = { 84,85,86,87 }; - - public static final int TRANSMIT_OFFSET_VHF_UHF_SIGN = 88; - - public static final int[] TRANSMIT_OFFSET = { 89,90,91,92,93,94,95,96,97,98, - 99,100,101 }; - - public IdentifierUpdateVUHF( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.IDENTIFIER_UPDATE_VHF_UHF_BANDS.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append(toString()); - - return sb.toString(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( " ID:" + getIdentifier() ); - sb.append( " BASE:" + getBaseFrequency() ); - sb.append( " BANDWIDTH:" + getBandwidth() ); - sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - - return sb.toString(); - } - - /** - * Channel bandwidth in hertz - */ - public int getBandwidth() - { - int bandwidth = mMessage.getInt( BANDWIDTH ); - - switch( bandwidth ) - { - case 4: - return 6250; - case 5: - return 12500; - default: - return 0; - } - } - - /** - * Transmit offset in hertz - */ - @Override - public long getTransmitOffset() - { - long offset = mMessage.getLong( TRANSMIT_OFFSET ) * - getChannelSpacing(); - - if( mMessage.get( TRANSMIT_OFFSET_VHF_UHF_SIGN ) ) - { - return offset; - } - else - { - return -offset; - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java deleted file mode 100644 index 9924e5b1..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.Response; - -public class LocationRegistrationResponse extends TSBKMessage -{ - public static final int[] REGISTRATION_RESPONSE = { 86,87 }; - public static final int[] GROUP_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103 }; - public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 }; - public static final int[] SITE_ID = { 112,113,114,115,116,117,118,119 }; - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public LocationRegistrationResponse( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.LOCATION_REGISTRATION_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " REGISTRATION:" + getResponse().name() ); - sb.append( " GROUP:" + getGroupAddress() ); - sb.append( " RFSS: " + getRFSSID() ); - sb.append( " SITE: " + getSiteID() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public Response getResponse() - { - return Response.fromValue( mMessage.getInt( REGISTRATION_RESPONSE ) ); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - public String getRFSSID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java deleted file mode 100644 index 497b5bd3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class MessageUpdate extends TSBKMessage -{ - public static final int[] SHORT_DATA_MESSAGE = { 80,81,82,83,84,85,86,87,88,89,90,91, - 92,93,94,95 }; - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public MessageUpdate( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.MESSAGE_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " MSG:" + getShortDataMessage() ); - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getShortDataMessage() - { - return mMessage.getHex( SHORT_DATA_MESSAGE, 4 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java deleted file mode 100644 index c19510c3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.P25NetworkCallsign; - -public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierReceiver -{ - public static final int[] LOCATION_REGISTRATION_AREA = {80, 81, 82, 83, 84, 85, 86, 87}; - public static final int[] WACN = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107}; - public static final int[] SYSTEM_ID = {108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] IDENTIFIER = {120, 121, 122, 123}; - public static final int[] CHANNEL = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SYSTEM_SERVICE_CLASS = {136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate; - - public NetworkStatusBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return Opcode.NETWORK_STATUS_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" WACN:" + getWACN()); - - sb.append(" SYS:" + getSystemID()); - - sb.append(" CTRL CHAN:" + getIdentifier() + "-" + getChannel()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" UP:" + getUplinkFrequency()); - - sb.append(" SYS SVC CLASS:" + - SystemService.toString(getSystemServiceClass())); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LOCATION_REGISTRATION_AREA, 2); - } - - public String getWACN() - { - return mMessage.getHex(WACN, 5); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getNetworkCallsign() - { - return P25NetworkCallsign.getCallsign(mMessage.getInt(WACN), - mMessage.getInt(SYSTEM_ID)); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public int getChannel() - { - return mMessage.getInt(CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - /* we're only expecting 1 identifier, so use whatever is received */ - mIdentifierUpdate = message; - } - - public int[] getIdentifiers() - { - int[] idens = new int[1]; - - idens[0] = getIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java deleted file mode 100644 index 9131f693..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Encryption; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class ProtectionParameterUpdate extends TSBKMessage -{ - public static final int[] ALGORITHM_ID = { 96,97,98,99,100,101,102,103 }; - public static final int[] KEY_ID = { 104,105,106,107,108,109,110,111,112, - 113,114,115,116,117,118,119 }; - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public ProtectionParameterUpdate( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.PROTECTION_PARAMETER_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - sb.append( " ENCRYPTION ALGORITHM:" + getAlgorithm().name() ); - - sb.append( " KEY:" + getKeyID() ); - - return sb.toString(); - } - - public Encryption getAlgorithm() - { - return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); - } - - public String getKeyID() - { - return mMessage.getHex( KEY_ID, 4 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java deleted file mode 100644 index 932128df..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.QueuedResponseReason; - -public class QueuedResponse extends TSBKMessage -{ - public static final int ADDITIONAL_INFORMATION_FLAG = 80; - public static final int[] SERVICE_TYPE = { 82,83,84,85,86,87 }; - public static final int[] REASON_CODE = { 88,89,90,91,92,93,94,95 }; - - public static final int[] CALL_OPTIONS = { 96,97,98,99,100,101,102,103 }; - public static final int[] SOURCE_GROUP = { 104,105,106,107,108,109,110,111 }; - public static final int[] SOURCE_ADDRESS = { 96,97,98,99,100,101,102,103,104, - 105,106,107,108,109,110,111 }; - - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public QueuedResponse( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.QUEUED_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TGT ADDR: " + getTargetAddress() ); - - sb.append( " REASON:" + getReason().name() ); - - return sb.toString(); - } - - public QueuedResponseReason getReason() - { - int code = mMessage.getInt( REASON_CODE ); - - return QueuedResponseReason.fromCode( code ); - } - - public boolean hasAdditionalInformation() - { - return mMessage.get( ADDITIONAL_INFORMATION_FLAG ); - } - - public Opcode getServiceType() - { - return Opcode.fromValue( mMessage.getInt( SERVICE_TYPE ) ); - } - - public String getSourceAddress() - { - switch( getServiceType() ) - { - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - return mMessage.getHex( SOURCE_GROUP, 4 ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - return "DELAY:" + ( mMessage.getInt( SOURCE_ADDRESS ) * 5 ) + "SECS"; - default: - break; - } - - return null; - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java deleted file mode 100644 index a2a72d8a..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java +++ /dev/null @@ -1,158 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class RFSSStatusBroadcast extends TSBKMessage implements IdentifierReceiver -{ - public static final int[] LOCATION_REGISTRATION_AREA = {80, 81, 82, 83, 84, 85, 86, 87}; - public static final int ACTIVE_NETWORK_CONNECTION_FLAG = 91; - public static final int[] SYSTEM_ID = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; - public static final int[] RFSS_ID = {104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SITE_ID = {112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] IDENTIFIER = {120, 121, 122, 123}; - public static final int[] CHANNEL = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SYSTEM_SERVICE_CLASS = {136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate; - - public RFSSStatusBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return Opcode.RFSS_STATUS_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" LRA:" + getLocationRegistrationArea()); - - sb.append(" SYSID:" + getSystemID()); - - sb.append(" RFSS:" + getRFSubsystemID()); - - sb.append(" SITE:" + getSiteID()); - - sb.append(" CHAN:" + getIdentifier() + "-" + getChannel()); - - sb.append(" DN:" + getDownlinkFrequency()); - - sb.append(" UP:" + getUplinkFrequency()); - - if(hasActiveNetworkConnection()) - { - sb.append(" ACTIVE-NETWORK-CONN"); - } - - sb.append(SystemService.toString(getSystemServiceClass())); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex(LOCATION_REGISTRATION_AREA, 2); - } - - public boolean hasActiveNetworkConnection() - { - return mMessage.get(ACTIVE_NETWORK_CONNECTION_FLAG); - } - - public String getSystemID() - { - return mMessage.getHex(SYSTEM_ID, 3); - } - - public String getRFSubsystemID() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifier() - { - return mMessage.getInt(IDENTIFIER); - } - - public int getChannel() - { - return mMessage.getInt(CHANNEL); - } - - public int getSystemServiceClass() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS); - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getDownlinkFrequency(getChannel()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdate != null) - { - return mIdentifierUpdate.getUplinkFrequency(getChannel()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - /* we're only expecting 1 identifier, so use whatever is received */ - mIdentifierUpdate = message; - } - - public int[] getIdentifiers() - { - int[] idens = new int[1]; - - idens[0] = getIdentifier(); - - return idens; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java deleted file mode 100644 index 9a54d794..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class RadioUnitMonitorCommand extends TSBKMessage -{ - public static final int[] TX_MULTIPLIER = { 94,95 }; - public static final int[] SOURCE_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public RadioUnitMonitorCommand( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.RADIO_UNIT_MONITOR_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " MULT:" + getTransmitMultiplier() ); - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public int getTransmitMultiplier() - { - return mMessage.getInt( TX_MULTIPLIER ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java deleted file mode 100644 index 3a9f9545..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.StackOperation; - -public class RoamingAddressCommand extends TSBKMessage -{ - public static final int[] STACK_OPERATION = { 80,81,82,83,84,85,86,87 }; - public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, - 101,102,103,104,105,106,107 }; - public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115,116, - 117,118,119 }; - public static final int[] TARGET_ID = { 120,121,122,123,124,125,126,127,128, - 129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public RoamingAddressCommand( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.ROAMING_ADDRESS_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( getStackOperation().name() ); - sb.append( " WACN:" + getWACN() ); - sb.append( " SYSTEM: " + getSystemID() ); - sb.append( " TGT ID: " + getTargetID() ); - - return sb.toString(); - } - - public StackOperation getStackOperation() - { - return StackOperation.fromValue( mMessage.getInt( STACK_OPERATION ) ); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getTargetID() - { - return mMessage.getHex( TARGET_ID, 6 ); - } - - @Override - public String getToID() - { - return getTargetID(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java deleted file mode 100644 index 15461a76..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java +++ /dev/null @@ -1,266 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class SecondaryControlChannelBroadcast extends TSBKMessage implements IdentifierReceiver, Comparable -{ - public static final int[] RFSS_ID = {80, 81, 82, 83, 84, 85, 86, 87}; - public static final int[] SITE_ID = {88, 89, 90, 91, 92, 93, 94, 95}; - public static final int[] IDENTIFIER_1 = {96, 97, 98, 99}; - public static final int[] CHANNEL_1 = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] SYSTEM_SERVICE_CLASS_1 = {112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] IDENTIFIER_2 = {120, 121, 122, 123}; - public static final int[] CHANNEL_2 = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; - public static final int[] SYSTEM_SERVICE_CLASS_2 = {136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdate1; - private IBandIdentifier mIdentifierUpdate2; - - public SecondaryControlChannelBroadcast(BinaryMessage message, - DataUnitID duid, - AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return Opcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); - - sb.append(" CHAN1:" + getIdentifier1() + "-" + getChannel1()); - - sb.append(" DN1:" + getDownlinkFrequency1()); - - sb.append(" UP1:" + getUplinkFrequency1()); - - sb.append(" SVC1:" + SystemService.toString(getSystemServiceClass1())); - - if(hasChannel2()) - { - sb.append(" CHAN2:" + getIdentifier2() + "-" + getChannel2()); - - sb.append(" DN2:" + getDownlinkFrequency2()); - - sb.append(" UP2:" + getUplinkFrequency2()); - - sb.append(" SVC2:" + SystemService.toString(getSystemServiceClass2())); - } - - return sb.toString(); - } - - public String getRFSS() - { - return mMessage.getHex(RFSS_ID, 2); - } - - public String getSiteID() - { - return mMessage.getHex(SITE_ID, 2); - } - - public int getIdentifier1() - { - return mMessage.getInt(IDENTIFIER_1); - } - - public int getChannel1() - { - return mMessage.getInt(CHANNEL_1); - } - - public int getSystemServiceClass1() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS_1); - } - - public int getIdentifier2() - { - return mMessage.getInt(IDENTIFIER_2); - } - - public int getChannel2() - { - return mMessage.getInt(CHANNEL_2); - } - - public int getSystemServiceClass2() - { - return mMessage.getInt(SYSTEM_SERVICE_CLASS_2); - } - - public boolean hasChannel2() - { - return getSystemServiceClass2() != 0; - } - - - public long getDownlinkFrequency1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.getDownlinkFrequency(getChannel1()); - } - - return 0; - } - - public long getUplinkFrequency1() - { - if(mIdentifierUpdate1 != null) - { - return mIdentifierUpdate1.getUplinkFrequency(getChannel1()); - } - - return 0; - } - - public long getDownlinkFrequency2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.getDownlinkFrequency(getChannel2()); - } - - return 0; - } - - public long getUplinkFrequency2() - { - if(mIdentifierUpdate2 != null) - { - return mIdentifierUpdate2.getUplinkFrequency(getChannel2()); - } - - return 0; - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getIdentifier1()) - { - mIdentifierUpdate1 = message; - } - - if(identifier == getIdentifier2()) - { - mIdentifierUpdate2 = message; - } - } - - public int[] getIdentifiers() - { - int[] idens; - - if(hasChannel2()) - { - idens = new int[2]; - - idens[0] = getIdentifier1(); - idens[1] = getIdentifier2(); - } - else - { - idens = new int[1]; - - idens[0] = getIdentifier1(); - } - - return idens; - } - - @Override - public int compareTo(SecondaryControlChannelBroadcast other) - { - if(other.getRFSS().contentEquals(getRFSS()) && - other.getSiteID().contentEquals(getSiteID()) && - other.getIdentifier1() == getIdentifier1() && - other.getChannel1() == getChannel1()) - { - return 0; - } - else if(other.getIdentifier1() < getIdentifier1()) - { - return -1; - } - else - { - return 1; - } - } - - @Override - public boolean equals(Object o) - { - if(this == o) - { - return true; - } - if(!(o instanceof SecondaryControlChannelBroadcast)) - { - return false; - } - - SecondaryControlChannelBroadcast that = (SecondaryControlChannelBroadcast)o; - - if(getRFSS() != that.getRFSS()) - { - return false; - } - if(getSiteID() != that.getSiteID()) - { - return false; - } - if(getIdentifier1() != that.getIdentifier1()) - { - return false; - } - - return getChannel1() == that.getChannel1(); - } - - @Override - public int hashCode() - { - int result = getRFSS() != null ? getRFSS().hashCode() : 0; - result = 31 * result + getSiteID() != null ? getSiteID().hashCode() : 0; - result = 31 * result + getIdentifier1(); - result = 31 * result + getChannel1(); - return result; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java deleted file mode 100644 index a8be1880..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SecondaryControlChannelBroadcastExplicit - extends SecondaryControlChannelBroadcast -{ - private final static Logger mLog = LoggerFactory.getLogger( - SecondaryControlChannelBroadcastExplicit.class ); - - public SecondaryControlChannelBroadcastExplicit( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " DOWNLINK:" + getIdentifier1() + "-" + getChannel1() ); - - sb.append( " " + getDownlinkFrequency1() ); - - sb.append( " SVC1:" + - SystemService.toString( getSystemServiceClass1() ) ); - - if( hasChannel2() ) - { - sb.append( " UPLINK:" + getIdentifier2() + "-" + getChannel2() ); - - sb.append( " " + getUplinkFrequency1() ); - - sb.append( " SVC2:" + - SystemService.toString( getSystemServiceClass2() ) ); - } - - return sb.toString(); - } - - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusQuery.java deleted file mode 100644 index 8986c317..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusQuery.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class StatusQuery extends TSBKMessage -{ - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public StatusQuery( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.STATUS_QUERY.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java deleted file mode 100644 index 370c7874..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class StatusUpdate extends TSBKMessage -{ - public static final int[] USER_STATUS = { 72,73,74,75,88,89,90,91 }; - public static final int[] UNIT_STATUS = { 92,93,94,95,96,97,98,99 }; - public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, - 167,168,169,170,171,184,185,186,187,188,189,190,190,192,193,194,195 }; - - public StatusUpdate( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.STATUS_UPDATE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " STATUS USER:" + getUserStatus() ); - sb.append( " UNIT:" + getUnitStatus() ); - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getUserStatus() - { - return mMessage.getHex( USER_STATUS, 2 ); - } - - public String getUnitStatus() - { - return mMessage.getHex( UNIT_STATUS, 2 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java deleted file mode 100644 index e7ed4a93..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java +++ /dev/null @@ -1,274 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.SimpleTimeZone; -import java.util.TimeZone; - -/** - * Sync Broadcast - used to broadcast FDMA-TDMA timing synchronization - * information so that a subscriber unit that is time synchronized on an FDMA - * control channel is also time synchronized with all TDMA traffic channels at - * the site. - */ -public class SyncBroadcast extends TSBKMessage -{ - public static final int[] RESERVED = { 80,81,82,83,84,85,86,87,88,89,90,91,92 }; - public static final int SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE = 93; - public static final int MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED = 94; - public static final int[] LEAP_SECOND_CORRECTION = { 95,96 }; - public static final int LOCAL_TIME_OFFSET_VALID = 97; - public static final int LOCAL_TIME_OFFSET_SIGN = 98; - public static final int[] LOCAL_TIME_OFFSET_HOURS = { 99,100,101,102 }; - public static final int LOCAL_TIME_OFFSET_HALF_HOUR = 103; - - public static final int[] YEAR = { 104,105,106,107,108,109,110 }; - public static final int[] MONTH = { 111,112,113,114 }; - public static final int[] DAY = { 115,116,117,118,119 }; - public static final int[] HOURS = { 120,121,122,123,124 }; - public static final int[] MINUTES = { 125,126,127,128,129,130 }; - public static final int[] MICRO_SLOTS = { 131,132,133,134,135,136,137,138, - 139,140,141,142,143 }; - public static final int[] TSBK_CRC = { 144,145,146,147,148,149,150,151,152, - 153,154,155,156,157,158,159 }; - - private static final DateFormat TIME_FORMATTER = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS Z" ); - private static final TimeZone NO_TIME_ZONE = new SimpleTimeZone( 0, "NONE" ); - - public SyncBroadcast( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.TDMA_SYNC_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SYSTEM TIME" ); - - if( isSystemTimeNotLockedToExternalReference() ) - { - sb.append( " UNLOCKED" ); - } - - sb.append( ":" ); - - TIME_FORMATTER.setTimeZone( getTimeZone() ); - sb.append( " " + TIME_FORMATTER.format( new Date( getSystemTime() ) ) ); - - sb.append( " LEAP-SECOND CORRECTION:" + getLeapSecondCorrection() + "mS" ); - - if( isMicroslotsLockedToMinuteRollover() ) - { - sb.append( " MICROSLOT-MINUTE ROLLOVER:SLOW" ); - } - else - { - sb.append( " MICROSLOT-MINUTE ROLLOVER:UNLOCKED" ); - } - - return sb.toString(); - } - - /** - * System Time (UTC) in milliseconds since java epoch - */ - public long getSystemTime() - { - Calendar cal = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); - - cal.clear(); - - cal.set( Calendar.YEAR, getYear() ); - cal.set( Calendar.MONTH, getMonth() - 1 ); - cal.set( Calendar.DAY_OF_MONTH, getDay() ); - cal.set( Calendar.HOUR_OF_DAY, getHours() ); - cal.set( Calendar.MINUTE, getMinutes() ); - cal.set( Calendar.MILLISECOND, getMilliSeconds() ); - - return cal.getTimeInMillis(); - } - - /** - * System Time not locked to external time reference indicator - */ - public boolean isSystemTimeNotLockedToExternalReference() - { - return mMessage.get( SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE ); - } - - /** - * Micro-slot-to-Minute rollover boundary lock indicator. - * - * Note: we invert the message value so that true indicates that the micro - * slots and minute rollovers are locked, whereas the ICD uses false to - * indicate a lock. - * - * @return true if the microslots rollover at each minute interval is locked - * to the system time minute rollover. - * - * @return false if the microslots and minutes rollover are not locked. This - * indicates that the microslots minute rollover is free rolling. - * - */ - public boolean isMicroslotsLockedToMinuteRollover() - { - return !mMessage.get( MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED ); - } - - /** - * Leap second correction value that should be applied to the system time. - * - * The leap-second correction field indicates the number of 2.5 millisecond - * units that must be added to the system time to account for the insertion - * of leap seconds when the system time is represented as UTC universal time. - * - * @return leap-second correction value in milliseconds 0.0-10.0 - */ - public double getLeapSecondCorrection() - { - return (double)mMessage.getInt( LEAP_SECOND_CORRECTION ) * 2.5d; - } - - - - /** - * Indicates the local time offset fields contain valid information. - */ - public boolean isValidLocalTimeOffset() - { - return !mMessage.get( LOCAL_TIME_OFFSET_VALID ); - } - - /** - * Local time zone offset from UTC universal time (zulu) when the VALID - * LOCAL TIME OFFSET flag indicates a valid offset. Otherwise, this method - * returns a static +00:00 indicating no local time offset. - */ - public TimeZone getTimeZone() - { - if( isValidLocalTimeOffset() ) - { - int offset = 0; - - offset += mMessage.getInt( LOCAL_TIME_OFFSET_HOURS ) * 3600000; - offset += mMessage.get( LOCAL_TIME_OFFSET_HALF_HOUR ) ? 1800000 : 0; - offset = mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? -offset : offset; - - return new SimpleTimeZone( offset, "LOCAL" ); - } - else - { - return NO_TIME_ZONE; - } - } - - /** - * System time - year. - * - * @return year in range 2000-2127 - */ - public int getYear() - { - return 2000 + mMessage.getInt( YEAR ); - } - - /** - * System time - month - * - * @return month 1-12 where 1=January and 12=December - */ - public int getMonth() - { - return mMessage.getInt( MONTH ); - } - - /** - * System time - day of month - * - * @return day of month in range 1-31 - */ - public int getDay() - { - return mMessage.getInt( DAY ); - } - - /** - * System time - hours - * - * @return hours in range 0 - 23 - */ - public int getHours() - { - return mMessage.getInt( HOURS ); - } - - /** - * System time - minutes - * - * @return minutes in range 0 - 59 - */ - public int getMinutes() - { - return mMessage.getInt( MINUTES ); - } - - /** - * System time - milli-seconds - * - * Note: sub-millisecond values are rounded to nearest millisecond unit to - * conform to java Date internal milli-second precision level. - * - * @return milli-seconds in range 0 - 59999; - */ - public int getMilliSeconds() - { - return (int)( (double)getMicroSlots() * 7.5 ); - } - - /** - * TDMA Micro Slots. - * - * Number of 7.5 millisecond micro slots since the last minute rollover, or - * since the last micro-slot counter rollover when the the micro-slot to - * minute rollover is unlocked. - * - * @see isMicroslotsLockedToMinuteRollover() method. - * - * This value supports mobile subscriber timing alignment with a TDMA traffic - * channel when a channel is granted from the control channels, so that when - * the mobile changes from the control channel to the traffic channel, it is - * already aligned with the traffic channel TDMA timing. - * - * Super frames occur every 360 ms (48 micro-slots) on FDMA and Ultra frames - * occur every 4 super frames on TDMA. - * - * Valid micro-slot values range 0-7999 and represent 60,000 - * milliseconds ( 8000 x 7.5 ms). - * - * @return number of 7.5 mS micro-slots since last minute or micro-slot - * rollover - */ - public int getMicroSlots() - { - return mMessage.getInt( MICRO_SLOTS ); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemService.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemService.java deleted file mode 100644 index 14e07314..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemService.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -public class SystemService -{ - public static boolean isCompositeControlChannel( int service ) - { - return ( service & 0x01 ) == 0x01; - } - - public static boolean isUpdateControlChannelOnly( int service ) - { - return ( service & 0x02 ) == 0x02; - } - - public static boolean isBackupControlChannelOnly( int service ) - { - return ( service & 0x04 ) == 0x04; - } - - public static boolean providesDataServiceRequests( int service ) - { - return ( service & 0x10 ) == 0x10; - } - - public static boolean providesVoiceServiceRequests( int service ) - { - return ( service & 0x20 ) == 0x20; - } - - public static boolean providesRegistrationServices( int service ) - { - return ( service & 0x40 ) == 0x40; - } - - public static boolean providesAuthenticationServices( int service ) - { - return ( service & 0x80 ) == 0x80; - } - - public static String toString( int service ) - { - StringBuilder sb = new StringBuilder(); - - sb.append( "CONTROL CHAN[" ); - - if( isCompositeControlChannel( service ) ) - { - sb.append( " COMPOSITE" ); - } - - if( isUpdateControlChannelOnly( service ) ) - { - sb.append( " UPDATE" ); - } - - if( isBackupControlChannelOnly( service ) ) - { - sb.append( " BACKUP" ); - } - - sb.append( " ] SERVICES[" ); - - if( providesAuthenticationServices( service ) ) - { - sb.append( " AUTHENTICATION" ); - } - - if( providesDataServiceRequests( service ) ) - { - sb.append( " DATA" ); - } - - if( providesRegistrationServices( service ) ) - { - sb.append( " REGISTRATION" ); - } - - if( providesVoiceServiceRequests( service ) ) - { - sb.append( " VOICE" ); - } - - sb.append( " ]" ); - - return sb.toString(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java deleted file mode 100644 index a722abfb..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.Service; - -import java.util.List; - -public class SystemServiceBroadcast extends TSBKMessage -{ - public static final int[] AVAILABLE_SERVICES = { 88,89,90,91,92,93,94,95, - 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SUPPORTED_SERVICES = { 112,113,114,115,116,117, - 118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135 }; - public static final int[] REQUEST_PRIORITY_LEVEL = { 136,137,138,139,140, - 141,142,143 }; - - public SystemServiceBroadcast( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.SYSTEM_SERVICE_BROADCAST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SERVICES AVAILABLE " ); - - sb.append( getAvailableServices() ); - - sb.append( " SUPPORTED " ); - - sb.append( getSupportedServices() ); - - return sb.toString(); - } - - public List getAvailableServices() - { - long bitmap = mMessage.getLong( AVAILABLE_SERVICES ); - - return Service.getServices( bitmap ); - } - - public List getSupportedServices() - { - long bitmap = mMessage.getLong( SUPPORTED_SERVICES ); - - return Service.getServices( bitmap ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java deleted file mode 100644 index b85ea059..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -public class TimeAndDateAnnouncement extends TSBKMessage -{ - public static final int VALID_DATE_INDICATOR = 80; - public static final int VALID_TIME_INDICATOR = 81; - public static final int VALID_LOCAL_TIME_OFFSET_INDICATOR = 82; - - public static final int LOCAL_TIME_OFFSET_SIGN = 84; - public static final int[] LOCAL_TIME_OFFSET = { 85,86,87,88,89,90,91,92, - 93,94,95 }; - public static final int[] MONTH = { 96,97,98,99 }; - public static final int[] DAY = { 100,101,102,103 }; - public static final int[] YEAR = { 105,106,107,108,109,110,111,112,113,114, - 115,116,117 }; - public static final int[] HOURS = { 120,121,122,123,124 }; - public static final int[] MINUTES = { 125,126,127,128,129,130 }; - public static final int[] SECONDS = { 131,132,133,134,135,136 }; - - private SimpleDateFormat mTimeFormatter = - new SimpleDateFormat( "EEE, d MMM yyyy HH:mm:ss Z" ); - - public TimeAndDateAnnouncement( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.TIME_DATE_ANNOUNCEMENT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( hasValidDate() ) - { - sb.append( " DATE-TIME:" ); - } - - sb.append( " " ); - sb.append( mTimeFormatter.format( new Date( getDateTimestamp() ) ) ); - - return sb.toString(); - } - - public long getDateTimestamp() - { - Calendar cal = new GregorianCalendar( getTimeZone() ); - - if( hasValidDate() ) - { - cal.set( Calendar.YEAR, getYear() ); - cal.set( Calendar.MONTH, getMonth() ); - cal.set( Calendar.DAY_OF_MONTH, getDay() ); - } - - if( hasValidTime() ) - { - cal.set( Calendar.HOUR, getHour() ); - cal.set( Calendar.MINUTE, getMinute() ); - cal.set( Calendar.SECOND, getSecond() ); - } - - return cal.getTimeInMillis(); - } - - public boolean hasValidDate() - { - return mMessage.get( VALID_DATE_INDICATOR ); - } - - public boolean hasValidTime() - { - return mMessage.get( VALID_TIME_INDICATOR ); - } - - public TimeZone getTimeZone() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "GMT " ); - - sb.append( mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? "-" : "+" ); - - int offsetMinutes = getLocalTimeOffset(); - - sb.append( (int)( offsetMinutes / 60 ) ); - - sb.append( " : " ); - - sb.append( offsetMinutes % 60 ); - - return TimeZone.getTimeZone( sb.toString() ); - } - - public boolean hasValidLocalTimeOffset() - { - return mMessage.get( VALID_LOCAL_TIME_OFFSET_INDICATOR ); - } - - /** - * Local Time Offset in minutes - */ - public int getLocalTimeOffset() - { - return mMessage.getInt( LOCAL_TIME_OFFSET ); - } - - public int getMonth() - { - return mMessage.getInt( MONTH ); - } - - public int getDay() - { - return mMessage.getInt( DAY ); - } - - public int getYear() - { - return mMessage.getInt( YEAR ); - } - - public int getHour() - { - return mMessage.getInt( HOURS ); - } - - public int getMinute() - { - return mMessage.getInt( MINUTES ); - } - - public int getSecond() - { - return mMessage.getInt( SECONDS ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java deleted file mode 100644 index 467a5bbf..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitDeregistrationAcknowledge extends TSBKMessage -{ - public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, - 101,102,103,104,105,106,107 }; - public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115, - 116,117,118,119 }; - public static final int[] SOURCE_ID = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public UnitDeregistrationAcknowledge( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_DEREGISTRATION_ACKNOWLEDGE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " WACN:" + getWACN() ); - sb.append( " SYSTEM:" + getSystemID() ); - sb.append( " SRC ID: " + getSourceID() ); - - return sb.toString(); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - @Override - public String getToID() - { - return getSourceID(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java deleted file mode 100644 index 00b15a9e..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitRegistrationCommand extends TSBKMessage -{ - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public UnitRegistrationCommand( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_REGISTRATION_COMMAND.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SRC ADDR: " + getSourceAddress() ); - sb.append( " TGT ADDR: " + getTargetAddress() ); - - return sb.toString(); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java deleted file mode 100644 index e0c3d5da..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.control; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; -import io.github.dsheirer.module.decode.p25.reference.Response; - -public class UnitRegistrationResponse extends TSBKMessage -{ - public static final int[] REGISTRATION_RESPONSE = { 82,83 }; - public static final int[] SYSTEM_ID = { 84,85,86,87,88,89,90,91,92,93,94,95 }; - public static final int[] SOURCE_ID = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public UnitRegistrationResponse( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_REGISTRATION_RESPONSE.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " REGISTRATION:" + getResponse().name() ); - sb.append( " SYSTEM:" + getSystemID() ); - sb.append( " SRC ID: " + getSourceID() ); - sb.append( " SRC ADDR: " + getSourceAddress() ); - - return sb.toString(); - } - - public Response getResponse() - { - return Response.fromValue( mMessage.getInt( REGISTRATION_RESPONSE ) ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getSourceAddress() - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getSourceAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java deleted file mode 100644 index 70d0bd65..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.GroupMultiChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupDataChannelAnnouncement extends GroupMultiChannelGrant -{ - public GroupDataChannelAnnouncement( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_DATA_CHANNEL_ANNOUNCEMENT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java deleted file mode 100644 index c71a9368..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.GroupChannelGrantExplicit; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupDataChannelAnnouncementExplicit extends GroupChannelGrantExplicit -{ - public GroupDataChannelAnnouncementExplicit( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java deleted file mode 100644 index 5ffbe530..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.GroupChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupDataChannelGrant extends GroupChannelGrant - -{ - public GroupDataChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_DATA_CHANNEL_GRANT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java deleted file mode 100644 index 7f0067a3..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.UnitChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class IndividualDataChannelGrant extends UnitChannelGrant -{ - public IndividualDataChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.INDIVIDUAL_DATA_CHANNEL_GRANT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPData.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPData.java deleted file mode 100644 index 870e27e0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPData.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.ServiceMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public abstract class SNDCPData extends ServiceMessage -{ - public static final int[] NETWORK_SERVICE_ACCESS_POINT_ID = { 84,85,86,87 }; - - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public SNDCPData( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - public String getNSAPI() - { - return mMessage.getHex( NETWORK_SERVICE_ACCESS_POINT_ID, 1 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java deleted file mode 100644 index a4ef3e3e..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java +++ /dev/null @@ -1,178 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData implements IdentifierReceiver -{ - public static final int AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR = 88; - public static final int REQUESTED_ACCESS_AVAILABLE_INDICATOR = 89; - public static final int[] TRANSMIT_IDENTIFIER = {96, 97, 98, 99}; - public static final int[] TRANSMIT_NUMBER = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; - public static final int[] RECEIVE_IDENTIFIER = {112, 113, 114, 115}; - public static final int[] RECEIVE_NUMBER = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; - public static final int[] DATA_ACCESS_CONTROL = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdateTransmit; - private IBandIdentifier mIdentifierUpdateReceive; - - public SNDCPDataChannelAnnouncementExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - if(isAutonomousAccessAvailable()) - { - sb.append(" AUTONOMOUS ACCESS"); - } - - if(isRequestedAccessAvailable()) - { - sb.append(" DOWNLINK:"); - sb.append(getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - - sb.append(" " + getUplinkFrequency()); - - sb.append(" UPLINK:"); - sb.append(getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - - sb.append(" " + getDownlinkFrequency()); - - sb.append(" DATA ACCESS CONTROL:"); - } - else - { - sb.append(" ACCESS DENIED TO DATA ACCESS CONTROL:"); - } - - sb.append(getDataAccessControl()); - - return sb.toString(); - } - - public boolean isAutonomousAccessAvailable() - { - return mMessage.get(AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR); - } - - public boolean isRequestedAccessAvailable() - { - return mMessage.get(REQUESTED_ACCESS_AVAILABLE_INDICATOR); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getDataAccessControl() - { - return mMessage.getHex(DATA_ACCESS_CONTROL, 4); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mIdentifierUpdateTransmit = message; - } - else if(identifier == getReceiveChannelIdentifier()) - { - mIdentifierUpdateReceive = message; - } - else - { - throw new IllegalArgumentException("unexpected channel band " - + "identifier [" + identifier + "]"); - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitChannelIdentifier(); - identifiers[1] = getReceiveChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdateTransmit != null) - { - return mIdentifierUpdateTransmit.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdateReceive != null) - { - mIdentifierUpdateReceive.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java deleted file mode 100644 index ccac4d50..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java +++ /dev/null @@ -1,164 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 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.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.IBandIdentifier; -import io.github.dsheirer.module.decode.p25.message.IdentifierReceiver; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierReceiver -{ - public static final int[] TRANSMIT_IDENTIFIER = {88, 89, 90, 91}; - public static final int[] TRANSMIT_NUMBER = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; - public static final int[] RECEIVE_IDENTIFIER = {104, 105, 106, 107}; - public static final int[] RECEIVE_NUMBER = {108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; - public static final int[] TARGET_ADDRESS = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, - 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; - - private IBandIdentifier mIdentifierUpdateTransmit; - private IBandIdentifier mIdentifierUpdateReceive; - - public SNDCPDataChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) - { - super(message, duid, aliasList); - } - - public boolean isTDMAChannel() - { - if(mIdentifierUpdateTransmit != null) - { - return mIdentifierUpdateTransmit.isTDMA(); - } - - return false; - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append(getMessageStub()); - - if(isEmergency()) - { - sb.append(" EMERGENCY"); - } - - sb.append(" UPLINK:"); - sb.append(getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); - - sb.append(" " + getUplinkFrequency()); - - sb.append(" DOWNLINK:"); - sb.append(getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); - - sb.append(" " + getDownlinkFrequency()); - - sb.append(" TGT:"); - sb.append(getTargetAddress()); - - return sb.toString(); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt(TRANSMIT_IDENTIFIER); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt(TRANSMIT_NUMBER); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt(RECEIVE_IDENTIFIER); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt(RECEIVE_NUMBER); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getTargetAddress() - { - return mMessage.getHex(TARGET_ADDRESS, 6); - } - - @Override - public void setIdentifierMessage(int identifier, IBandIdentifier message) - { - if(identifier == getTransmitChannelIdentifier()) - { - mIdentifierUpdateTransmit = message; - } - else if(identifier == getReceiveChannelIdentifier()) - { - mIdentifierUpdateReceive = message; - } - else - { - throw new IllegalArgumentException("unexpected channel band " - + "identifier [" + identifier + "]"); - } - } - - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[2]; - - identifiers[0] = getTransmitChannelIdentifier(); - identifiers[1] = getReceiveChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - if(mIdentifierUpdateTransmit != null) - { - return mIdentifierUpdateTransmit.getDownlinkFrequency(getTransmitChannelNumber()); - } - - return 0; - } - - public long getUplinkFrequency() - { - if(mIdentifierUpdateReceive != null) - { - return mIdentifierUpdateReceive.getUplinkFrequency(getReceiveChannelNumber()); - } - - return 0; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java deleted file mode 100644 index 1219f873..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.data; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; - -public class SNDCPDataPageRequest extends SNDCPData -{ - public static final int[] DATA_ACCESS_CONTROL = { 104,105,106,107,108,109, - 110,111,112,113,114,115,116,117,118,119 }; - - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public SNDCPDataPageRequest( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " TGT:" ); - sb.append( getTargetAddress() ); - - sb.append( " DAC:" ); - sb.append( getDataAccessControl() ); - - return sb.toString(); - } - - public String getDataAccessControl() - { - return mMessage.getHex( DATA_ACCESS_CONTROL, 4 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java deleted file mode 100644 index fb743fec..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.GroupChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupVoiceChannelGrant extends GroupChannelGrant -{ - public GroupVoiceChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_VOICE_CHANNEL_GRANT.getDescription(); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java deleted file mode 100644 index 082c499c..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.GroupMultiChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupVoiceChannelGrantUpdate extends GroupMultiChannelGrant -{ - public GroupVoiceChannelGrantUpdate( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java deleted file mode 100644 index 066f3a75..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.GroupChannelGrantExplicit; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class GroupVoiceChannelGrantUpdateExplicit extends GroupChannelGrantExplicit -{ - public GroupVoiceChannelGrantUpdateExplicit( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java deleted file mode 100644 index c0944228..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Digit; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -import java.util.ArrayList; -import java.util.List; - -public class TelephoneInterconnectAnswerRequest extends TSBKMessage -{ - public static final int[] DIGIT_1 = { 80,81,82,83 }; - public static final int[] DIGIT_2 = { 84,85,86,87 }; - public static final int[] DIGIT_3 = { 88,89,90,91 }; - public static final int[] DIGIT_4 = { 92,93,94,95 }; - public static final int[] DIGIT_5 = { 96,97,98,99 }; - public static final int[] DIGIT_6 = { 100,101,102,103 }; - public static final int[] DIGIT_7 = { 104,105,106,107 }; - public static final int[] DIGIT_8 = { 108,109,110,111 }; - public static final int[] DIGIT_9 = { 112,113,114,115 }; - public static final int[] DIGIT_10 = { 116,117,118,119 }; - - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public TelephoneInterconnectAnswerRequest( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_ANSWER_REQUEST.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " TEL:" ); - sb.append( getTelephoneNumber() ); - - return sb.toString(); - } - - public String getTelephoneNumber() - { - List digits = new ArrayList(); - - digits.add( mMessage.getInt( DIGIT_1 ) ); - digits.add( mMessage.getInt( DIGIT_2 ) ); - digits.add( mMessage.getInt( DIGIT_3 ) ); - digits.add( mMessage.getInt( DIGIT_4 ) ); - digits.add( mMessage.getInt( DIGIT_5 ) ); - digits.add( mMessage.getInt( DIGIT_6 ) ); - digits.add( mMessage.getInt( DIGIT_7 ) ); - digits.add( mMessage.getInt( DIGIT_8 ) ); - digits.add( mMessage.getInt( DIGIT_9 ) ); - digits.add( mMessage.getInt( DIGIT_10 ) ); - - return Digit.decode( digits ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java deleted file mode 100644 index 16725c33..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.UnitChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant -{ - public static final int[] CALL_TIMER = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111 }; - - public TelephoneInterconnectVoiceChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " ADDR:" + getSourceAddress() ); - - sb.append( " CHAN:" ); - sb.append( getChannel() ); - - sb.append( " CALL TIMER:" ); - sb.append( getCallTimer() ); - - sb.append( " SECS TO:" ); - sb.append( getToID() ); - - return sb.toString(); - } - - public String getAddress() - { - return getSourceAddress(); - } - - /** - * Call timer in seconds - */ - public int getCallTimer() - { - int units = mMessage.getInt( CALL_TIMER ); - - return (int)( units / 10 ); - } - - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java deleted file mode 100644 index db6b95e7..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class TelephoneInterconnectVoiceChannelGrantUpdate - extends TelephoneInterconnectVoiceChannelGrant -{ - - public TelephoneInterconnectVoiceChannelGrantUpdate(BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java deleted file mode 100644 index 5240d7d7..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.UnitChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitToUnitAnswerRequest extends UnitChannelGrant -{ - public UnitToUnitAnswerRequest( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_TO_UNIT_ANSWER_REQUEST.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java deleted file mode 100644 index 6ac7541d..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.UnitChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitToUnitVoiceChannelGrant extends UnitChannelGrant -{ - public UnitToUnitVoiceChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " SOURCE UNIT:" ); - sb.append( getSourceAddress() ); - - sb.append( " TARGET UNIT:" ); - sb.append( getTargetAddress() ); - - sb.append( " CHAN:" + getChannelIdentifier() + "-" + getChannelNumber() ); - sb.append( " DN:" + getDownlinkFrequency() ); - sb.append( " UP:" + getUplinkFrequency() ); - - return sb.toString(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java deleted file mode 100644 index 29e23e97..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.osp.voice; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.module.decode.p25.message.tsbk.UnitChannelGrant; -import io.github.dsheirer.module.decode.p25.reference.DataUnitID; -import io.github.dsheirer.module.decode.p25.reference.Opcode; - -public class UnitToUnitVoiceChannelGrantUpdate extends UnitChannelGrant -{ - public UnitToUnitVoiceChannelGrantUpdate( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/AuthenticationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/AuthenticationQuery.java new file mode 100644 index 00000000..e633740e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/AuthenticationQuery.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit acknowledge response + */ +public class AuthenticationQuery extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public AuthenticationQuery(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CallAlertRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CallAlertRequest.java new file mode 100644 index 00000000..d89fb325 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CallAlertRequest.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call alert request + */ +public class CallAlertRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public CallAlertRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CancelServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CancelServiceRequest.java new file mode 100644 index 00000000..1542ce50 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/CancelServiceRequest.java @@ -0,0 +1,104 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.CancelReason; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Direction; + +import java.util.ArrayList; +import java.util.List; + +/** + * Cancel Service Request + */ +public class CancelServiceRequest extends ISPMessage +{ + private static final int ADDITIONAL_INFORMATION_VALID_FLAG = 16; + private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23}; + private static final int[] REASON_CODE = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ADDITIONAL_INFORMATION = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private CancelReason mCancelReason; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public CancelServiceRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" REASON:").append(getCancelReason()); + return sb.toString(); + } + + public CancelReason getCancelReason() + { + if(mCancelReason == null) + { + mCancelReason = CancelReason.fromCode(getMessage().getInt(REASON_CODE)); + } + + return mCancelReason; + } + + /** + * Service type for the cancel request + */ + public Opcode getServiceType() + { + return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.INBOUND, getVendor()); + } + + /** + * Additional details about the cancel request + */ + public String getAdditionalInformation() + { + return getMessage().getHex(ADDITIONAL_INFORMATION, 6); + } + + /** + * Indicates if the additional information field contains information + */ + public boolean hasAdditionalInformation() + { + return getMessage().get(ADDITIONAL_INFORMATION_VALID_FLAG); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/EmergencyAlarmRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/EmergencyAlarmRequest.java new file mode 100644 index 00000000..e61a96d9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/EmergencyAlarmRequest.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Emergency alarm request. + */ +public class EmergencyAlarmRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public EmergencyAlarmRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + return sb.toString(); + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ExtendedFunctionResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ExtendedFunctionResponse.java new file mode 100644 index 00000000..4ec91f85 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ExtendedFunctionResponse.java @@ -0,0 +1,88 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.ExtendedFunction; + +import java.util.ArrayList; +import java.util.List; + +/** + * Extended function response. + */ +public class ExtendedFunctionResponse extends ISPMessage +{ + private static final int[] FUNCTION = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ARGUMENTS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private ExtendedFunction mExtendedFunction; + private String mArguments; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public ExtendedFunctionResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" FUNCTION:").append(getExtendedFunction()); + sb.append(" ARGUMENTS:").append(getArguments()); + return sb.toString(); + } + + public ExtendedFunction getExtendedFunction() + { + if(mExtendedFunction == null) + { + mExtendedFunction = ExtendedFunction.fromValue(getMessage().getInt(FUNCTION)); + } + + return mExtendedFunction; + } + + public String getArguments() + { + if(mArguments == null) + { + mArguments = getMessage().getHex(ARGUMENTS, 6).toUpperCase(); + } + + return mArguments; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/FrequencyBandUpdateRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/FrequencyBandUpdateRequest.java new file mode 100644 index 00000000..4b8cc16e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/FrequencyBandUpdateRequest.java @@ -0,0 +1,83 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Identifier update request + */ +public class FrequencyBandUpdateRequest extends ISPMessage +{ + private static final int REBROADCAST_ALL_FREQUENCY_BANDS_FLAG = 16; + private static final int[] RESERVED_FLAGS = {17, 18, 19}; + private static final int[] FREQUENCY_BAND = {20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public FrequencyBandUpdateRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + if(isAllFrequencyBands()) + { + sb.append(" REBROADCAST ALL FREQUENCY BANDS"); + } + else + { + sb.append(" REBROADCAST FREQUENCY BAND:").append(getFrequencyBand()); + } + return sb.toString(); + } + + public boolean isAllFrequencyBands() + { + return getMessage().get(REBROADCAST_ALL_FREQUENCY_BANDS_FLAG); + } + + public int getFrequencyBand() + { + return getMessage().getInt(FREQUENCY_BAND); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationQueryResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationQueryResponse.java new file mode 100644 index 00000000..f745139a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationQueryResponse.java @@ -0,0 +1,92 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnnouncementTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group affiliation query response. + */ +public class GroupAffiliationQueryResponse extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] ANNOUNCEMENT_GROUP_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mAnnouncementGroupAddress; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupAffiliationQueryResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" ANNOUNCEMENT GROUP:").append(getAnnouncementGroupAddress()); + return sb.toString(); + } + + public Identifier getAnnouncementGroupAddress() + { + if(mAnnouncementGroupAddress == null) + { + mAnnouncementGroupAddress = APCO25AnnouncementTalkgroup.create(getMessage().getInt(ANNOUNCEMENT_GROUP_ADDRESS)); + } + + return mAnnouncementGroupAddress; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getAnnouncementGroupAddress()); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationRequest.java new file mode 100644 index 00000000..1d04f2b2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupAffiliationRequest.java @@ -0,0 +1,91 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group affiliation request + */ +public class GroupAffiliationRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; + private static final int[] SYSTEM_ID = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ID = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mSystemId; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupAffiliationRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" SYSTEM:").append(getSystemId()); + return sb.toString(); + } + + public Identifier getSystemId() + { + if(mSystemId == null) + { + mSystemId = APCO25System.create(getMessage().getInt(SYSTEM_ID)); + } + + return mSystemId; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ID)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSystemId()); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupDataServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupDataServiceRequest.java new file mode 100644 index 00000000..a8ccb21c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupDataServiceRequest.java @@ -0,0 +1,93 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group data service request. + */ +public class GroupDataServiceRequest extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupDataServiceRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupVoiceServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupVoiceServiceRequest.java new file mode 100644 index 00000000..a5f57633 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/GroupVoiceServiceRequest.java @@ -0,0 +1,93 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice call service request. + */ +public class GroupVoiceServiceRequest extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupVoiceServiceRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/IndividualDataServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/IndividualDataServiceRequest.java new file mode 100644 index 00000000..3217d541 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/IndividualDataServiceRequest.java @@ -0,0 +1,94 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Individual data service request. + */ +public class IndividualDataServiceRequest extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public IndividualDataServiceRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/LocationRegistrationRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/LocationRegistrationRequest.java new file mode 100644 index 00000000..3dd5821c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/LocationRegistrationRequest.java @@ -0,0 +1,115 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.Capability; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Location registration request + */ +public class LocationRegistrationRequest extends ISPMessage +{ + private static final int EMERGENCY_FLAG = 16; + private static final int[] CAPABILITY = {17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] LRA = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Capability mCapability; + private Identifier mLocationRegistrationArea; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public LocationRegistrationRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" CAPABILITY:").append(getCapability()); + return sb.toString(); + } + + public boolean isEmergency() + { + return getMessage().get(EMERGENCY_FLAG); + } + + public Capability getCapability() + { + if(mCapability == null) + { + mCapability = new Capability(getMessage().getInt(CAPABILITY)); + } + + return mCapability; + } + + public Identifier getLocationRegistrationArea() + { + if(mLocationRegistrationArea == null) + { + mLocationRegistrationArea = APCO25Lra.create(getMessage().getInt(LRA)); + } + + return mLocationRegistrationArea; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/MessageUpdateRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/MessageUpdateRequest.java new file mode 100644 index 00000000..5dbc5a19 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/MessageUpdateRequest.java @@ -0,0 +1,83 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Message update request + */ +public class MessageUpdateRequest extends ISPMessage +{ + private static final int[] MESSAGE = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public MessageUpdateRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" SHORT DATA MESSAGE:").append(getShortDataMessage()); + return sb.toString(); + } + + public String getShortDataMessage() + { + return getMessage().getHex(MESSAGE, 4); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ProtectionParameterRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ProtectionParameterRequest.java new file mode 100644 index 00000000..98fdc1c0 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/ProtectionParameterRequest.java @@ -0,0 +1,91 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Protection parameter request + */ +public class ProtectionParameterRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public ProtectionParameterRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + return sb.toString(); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ID)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RadioUnitMonitorRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RadioUnitMonitorRequest.java new file mode 100644 index 00000000..797d6397 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RadioUnitMonitorRequest.java @@ -0,0 +1,88 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Radio unit monitor request + */ +public class RadioUnitMonitorRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; + private static final int[] TX_MULTIPLIER = {30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public RadioUnitMonitorRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" TX MULTIPLIER:").append(getTxMultiplier()); + return sb.toString(); + } + + /** + * Multipler for a value stored in the radio that determines how long the target address radio will be + * keyed for monitoring. + */ + public int getTxMultiplier() + { + return getMessage().getInt(TX_MULTIPLIER); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressRequest.java new file mode 100644 index 00000000..f7fdd530 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressRequest.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Roaming address request + */ +public class RoamingAddressRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public RoamingAddressRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressResponse.java new file mode 100644 index 00000000..38707b7f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/RoamingAddressResponse.java @@ -0,0 +1,107 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Roaming address response + */ +public class RoamingAddressResponse extends ISPMessage +{ + private static final int LAST_MESSAGE_FLAG = 16; + private static final int[] MESSAGE_SEQUENCE_NUMBER = {20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public RoamingAddressResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" MESSAGE NUMBER:").append(getMessageSequenceNumber()); + if(isLastMessage()) + { + sb.append("-FINAL "); + } + return sb.toString(); + } + + public boolean isLastMessage() + { + return getMessage().get(LAST_MESSAGE_FLAG); + } + + public int getMessageSequenceNumber() + { + return getMessage().getInt(MESSAGE_SEQUENCE_NUMBER); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ID)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataChannelRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataChannelRequest.java new file mode 100644 index 00000000..19f854f9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataChannelRequest.java @@ -0,0 +1,102 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * SNDCP Data Channel Requenst + */ +public class SNDCPDataChannelRequest extends ISPMessage +{ + private static final int[] DATA_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] DATA_ACCESS_CONTROL = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] RESERVED = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mDataServiceOptions; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SNDCPDataChannelRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" ").append(getDataServiceOptions()); + sb.append(" DAC:").append(getDataAccessControl()); + return sb.toString(); + } + + public DataServiceOptions getDataServiceOptions() + { + if(mDataServiceOptions == null) + { + mDataServiceOptions = new DataServiceOptions(getMessage().getInt(DATA_SERVICE_OPTIONS)); + } + + return mDataServiceOptions; + } + + public int getDataAccessControl() + { + return getMessage().getInt(DATA_ACCESS_CONTROL); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataPageResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataPageResponse.java new file mode 100644 index 00000000..68573e4e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPDataPageResponse.java @@ -0,0 +1,110 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.AnswerResponse; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * SNDCP Data Channel Requenst + */ +public class SNDCPDataPageResponse extends ISPMessage +{ + private static final int[] DATA_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] ANSWER_RESPONSE = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] DATA_ACCESS_CONTROL = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] RESERVED = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mDataServiceOptions; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SNDCPDataPageResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" RESPONSE:").append(getAnswerResponse()); + sb.append(" ").append(getDataServiceOptions()); + sb.append(" DAC:").append(getDataAccessControl()); + return sb.toString(); + } + + public AnswerResponse getAnswerResponse() + { + return AnswerResponse.fromValue(getMessage().getInt(ANSWER_RESPONSE)); + } + + public DataServiceOptions getDataServiceOptions() + { + if(mDataServiceOptions == null) + { + mDataServiceOptions = new DataServiceOptions(getMessage().getInt(DATA_SERVICE_OPTIONS)); + } + + return mDataServiceOptions; + } + + public int getDataAccessControl() + { + return getMessage().getInt(DATA_ACCESS_CONTROL); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPReconnectRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPReconnectRequest.java new file mode 100644 index 00000000..47387bf1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/SNDCPReconnectRequest.java @@ -0,0 +1,109 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * SNDCP Data Channel Requenst + */ +public class SNDCPReconnectRequest extends ISPMessage +{ + private static final int[] DATA_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] DATA_ACCESS_CONTROL = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int DATA_TO_SEND_FLAG = 40; + private static final int[] RESERVED = {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mDataServiceOptions; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SNDCPReconnectRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(hasDataToSend() ? " HAS DATA TO SEND" : " HAS NO DATA TO SEND"); + sb.append(" ").append(getDataServiceOptions()); + sb.append(" DAC:").append(getDataAccessControl()); + return sb.toString(); + } + + public boolean hasDataToSend() + { + return getMessage().get(DATA_TO_SEND_FLAG); + } + + public DataServiceOptions getDataServiceOptions() + { + if(mDataServiceOptions == null) + { + mDataServiceOptions = new DataServiceOptions(getMessage().getInt(DATA_SERVICE_OPTIONS)); + } + + return mDataServiceOptions; + } + + public int getDataAccessControl() + { + return getMessage().getInt(DATA_ACCESS_CONTROL); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryRequest.java new file mode 100644 index 00000000..35c9337d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryRequest.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status query request + */ +public class StatusQueryRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public StatusQueryRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryResponse.java new file mode 100644 index 00000000..e93c83d4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusQueryResponse.java @@ -0,0 +1,105 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status query response + */ +public class StatusQueryResponse extends ISPMessage +{ + private static final int[] UNIT_STATUS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] USER_STATUS = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mUnitStatus; + private Identifier mUserStatus; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public StatusQueryResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" UNIT STATUS:").append(getUnitStatus()); + sb.append(" USER STATUS:").append(getUserStatus()); + return sb.toString(); + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null) + { + mUnitStatus = APCO25Status.createUnitStatus(getMessage().getInt(UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null) + { + mUserStatus = APCO25Status.createUserStatus(getMessage().getInt(USER_STATUS)); + } + + return mUserStatus; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getUnitStatus()); + mIdentifiers.add(getUserStatus()); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusUpdateRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusUpdateRequest.java new file mode 100644 index 00000000..76529a60 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/StatusUpdateRequest.java @@ -0,0 +1,105 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status update request + */ +public class StatusUpdateRequest extends ISPMessage +{ + private static final int[] UNIT_STATUS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] USER_STATUS = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mUnitStatus; + private Identifier mUserStatus; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public StatusUpdateRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" UNIT STATUS:").append(getUnitStatus()); + sb.append(" USER STATUS:").append(getUserStatus()); + return sb.toString(); + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null) + { + mUnitStatus = APCO25Status.createUnitStatus(getMessage().getInt(UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null) + { + mUserStatus = APCO25Status.createUserStatus(getMessage().getInt(USER_STATUS)); + } + + return mUserStatus; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getUnitStatus()); + mIdentifiers.add(getUserStatus()); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectAnswerResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectAnswerResponse.java new file mode 100644 index 00000000..d1f66ec8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectAnswerResponse.java @@ -0,0 +1,90 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.AnswerResponse; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Telephone interconnect answer response + */ +public class TelephoneInterconnectAnswerResponse extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] ANSWER_RESPONSE = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] RESERVED = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public TelephoneInterconnectAnswerResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" RESPONSE:").append(getAnswerResponse()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Response value + */ + public AnswerResponse getAnswerResponse() + { + return AnswerResponse.fromValue(getMessage().getInt(ANSWER_RESPONSE)); + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectPstnRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectPstnRequest.java new file mode 100644 index 00000000..a5e91267 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/TelephoneInterconnectPstnRequest.java @@ -0,0 +1,95 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.telephone.APCO25TelephoneNumber; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implicit Dialing Telephone Interconnect request. The telephone number (PSTN) is a predefined system value and is + * identified by the PSTN address value. + */ +public class TelephoneInterconnectPstnRequest extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47}; + private static final int[] PSTN_ADDRESS = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mPstnAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public TelephoneInterconnectPstnRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getPstnAddress()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getPstnAddress() + { + if(mPstnAddress == null) + { + mPstnAddress = APCO25TelephoneNumber.createTo("PSTN ENTRY #" + getMessage().getInt(PSTN_ADDRESS)); + } + + return mPstnAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getPstnAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitAcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitAcknowledgeResponse.java new file mode 100644 index 00000000..130d34ee --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitAcknowledgeResponse.java @@ -0,0 +1,90 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit acknowledge response + */ +public class UnitAcknowledgeResponse extends ISPMessage +{ + private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitAcknowledgeResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ACKNOWLEDGING:").append(getAcknowledgedServiceType()); + return sb.toString(); + } + + /** + * Opcode representing the service type that is being acknowledged by the radio unit. + */ + public Opcode getAcknowledgedServiceType() + { + return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.OUTBOUND, Vendor.STANDARD); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitDeRegistrationRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitDeRegistrationRequest.java new file mode 100644 index 00000000..a49bdefa --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitDeRegistrationRequest.java @@ -0,0 +1,91 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit registration request + */ +public class UnitDeRegistrationRequest extends ISPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitDeRegistrationRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + return sb.toString(); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ID)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitRegistrationRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitRegistrationRequest.java new file mode 100644 index 00000000..0fb085df --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitRegistrationRequest.java @@ -0,0 +1,115 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.Capability; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit registration request + */ +public class UnitRegistrationRequest extends ISPMessage +{ + private static final int EMERGENCY_FLAG = 16; + private static final int[] CAPABILITY = {17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Capability mCapability; + private Identifier mWACN; + private Identifier mSystem; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitRegistrationRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + sb.append(" FM:").append(getSourceAddress()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" CAPABILITY:").append(getCapability()); + return sb.toString(); + } + + public boolean isEmergency() + { + return getMessage().get(EMERGENCY_FLAG); + } + + public Capability getCapability() + { + if(mCapability == null) + { + mCapability = new Capability(getMessage().getInt(CAPABILITY)); + } + + return mCapability; + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ID)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceAnswerResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceAnswerResponse.java new file mode 100644 index 00000000..5bdfeb16 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceAnswerResponse.java @@ -0,0 +1,104 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.AnswerResponse; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Response to a unit-to-unit answer request + */ +public class UnitToUnitVoiceServiceAnswerResponse extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] ANSWER_RESPONSE = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitToUnitVoiceServiceAnswerResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" RESPONSE:").append(getAnswerResponse()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Response value + */ + public AnswerResponse getAnswerResponse() + { + return AnswerResponse.fromValue(getMessage().getInt(ANSWER_RESPONSE)); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceRequest.java new file mode 100644 index 00000000..192f1b43 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnitToUnitVoiceServiceRequest.java @@ -0,0 +1,94 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Voice call service request between two specified subscriber units. + */ +public class UnitToUnitVoiceServiceRequest extends ISPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mTargetId; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitToUnitVoiceServiceRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetId()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getTargetId() + { + if(mTargetId == null) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ID)); + } + + return mTargetId; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetId()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnknownISPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnknownISPMessage.java new file mode 100644 index 00000000..c74c1f33 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/isp/UnknownISPMessage.java @@ -0,0 +1,38 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown/Unrecognized opcode message. + */ +public class UnknownISPMessage extends ISPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnknownISPMessage(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" **UNRECOGNIZED ISP OPCODE**"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AcknowledgeResponse.java new file mode 100644 index 00000000..b8d992c4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AcknowledgeResponse.java @@ -0,0 +1,180 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Acknowledge response + */ +public class AcknowledgeResponse extends OSPMessage +{ + private static final int ADDITIONAL_INFORMATION_FLAG = 16; + private static final int EXTENDED_INFORMATION_FLAG = 17; + private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mWACN; + private Identifier mSystemId; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public AcknowledgeResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + if(hasSourceAddress()) + { + sb.append(" FM:").append(getSourceAddress()); + } + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ACKNOWLEDGING:").append(getAcknowledgedServiceType()); + if(hasWACN()) + { + sb.append(" WACN:").append(getWACN()); + } + if(hasSystem()) + { + sb.append(" SYSTEM:").append(getSystemId()); + } + return sb.toString(); + } + + private boolean hasAdditionalInformation() + { + return getMessage().get(ADDITIONAL_INFORMATION_FLAG); + } + + private boolean isExtendedSystemInformation() + { + return getMessage().get(EXTENDED_INFORMATION_FLAG); + } + + public boolean hasWACN() + { + return hasAdditionalInformation() && isExtendedSystemInformation(); + } + + public Identifier getWACN() + { + if(hasWACN()) + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + return null; + } + + public boolean hasSystem() + { + return hasAdditionalInformation() && isExtendedSystemInformation(); + } + + public Identifier getSystemId() + { + if(hasSystem()) + { + if(mSystemId == null) + { + mSystemId = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystemId; + } + + return null; + } + + /** + * Opcode representing the service type that is being acknowledged by the radio unit. + */ + public Opcode getAcknowledgedServiceType() + { + return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.INBOUND, Vendor.STANDARD); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public boolean hasSourceAddress() + { + return hasAdditionalInformation() && !isExtendedSystemInformation(); + } + + public Identifier getSourceAddress() + { + if(hasSourceAddress()) + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + return null; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + if(hasSourceAddress()) + { + mIdentifiers.add(getSourceAddress()); + } + + if(hasWACN()) + { + mIdentifiers.add(getWACN()); + } + if(hasSystem()) + { + mIdentifiers.add(getSystemId()); + } + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AdjacentStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AdjacentStatusBroadcast.java new file mode 100644 index 00000000..6d574a04 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AdjacentStatusBroadcast.java @@ -0,0 +1,210 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Adjacent status broadcast - neighbor sites + */ +public class AdjacentStatusBroadcast extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] LOCATION_REGISTRATION_AREA = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int CONVENTIONAL_CHANNEL_FLAG = 24; + private static final int SITE_FAILURE_FLAG = 25; + private static final int VALID_INFORMATION_FLAG = 26; + private static final int ACTIVE_NETWORK_CONNECTION_TO_RFSS_CONTROLLER_FLAG = 27; + private static final int[] SYSTEM = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] RFSS = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SITE = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] FREQUENCY_BAND = {56, 57, 58, 59}; + private static final int[] CHANNEL_NUMBER = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] SYSTEM_SERVICE_CLASS = {72, 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mLocationRegistrationArea; + private Identifier mSystem; + private Identifier mSite; + private Identifier mRfss; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + private List mIdentifiers; + private List mSiteFlags; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public AdjacentStatusBroadcast(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" RFSS:").append(getRfss()); + sb.append(" SITE:").append(getSite()); + sb.append(" FLAGS ").append(getSiteFlags()); + sb.append(" SERVICES ").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public List getSiteFlags() + { + if(mSiteFlags == null) + { + mSiteFlags = new ArrayList<>(); + + if(isConventionalChannel()) + { + mSiteFlags.add("CONVENTIONAL CHANNEL"); + } + + if(isFailedConditionSite()) + { + mSiteFlags.add("FAILURE CONDITION"); + } + + if(isValidSiteInformation()) + { + mSiteFlags.add("VALID INFORMATION"); + } + + if(isActiveNetworkConnectionToRfssControllerSite()) + { + mSiteFlags.add("ACTIVE RFSS CONNECTION"); + } + } + + return mSiteFlags; + } + + /** + * Indicates if the channel is a conventional repeater channel + */ + public boolean isConventionalChannel() + { + return getMessage().get(CONVENTIONAL_CHANNEL_FLAG); + } + + /** + * Indicates if the site is in a failure condition + */ + public boolean isFailedConditionSite() + { + return getMessage().get(SITE_FAILURE_FLAG); + } + + /** + * Indicates if the site informaiton is valid + */ + public boolean isValidSiteInformation() + { + return getMessage().get(VALID_INFORMATION_FLAG); + } + + /** + * Indicates if the site has an active network connection to the RFSS controller + */ + public boolean isActiveNetworkConnectionToRfssControllerSite() + { + return getMessage().get(ACTIVE_NETWORK_CONNECTION_TO_RFSS_CONTROLLER_FLAG); + } + + public Identifier getLocationRegistrationArea() + { + if(mLocationRegistrationArea == null) + { + mLocationRegistrationArea = APCO25Lra.create(getMessage().getInt(LOCATION_REGISTRATION_AREA)); + } + + return mLocationRegistrationArea; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public Identifier getRfss() + { + if(mRfss == null) + { + mRfss = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRfss; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SYSTEM_SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getSite()); + mIdentifiers.add(getRfss()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AuthenticationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AuthenticationCommand.java new file mode 100644 index 00000000..7a20cd41 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/AuthenticationCommand.java @@ -0,0 +1,89 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Authentication command + */ +public class AuthenticationCommand extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mTargetId; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public AuthenticationCommand(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetId()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + return sb.toString(); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetId()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/CallAlert.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/CallAlert.java new file mode 100644 index 00000000..88dbfc58 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/CallAlert.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call alert + */ +public class CallAlert extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceId; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public CallAlert(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceId()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceId() + { + if(mSourceId == null) + { + mSourceId = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ID)); + } + + return mSourceId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceId()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/DenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/DenyResponse.java new file mode 100644 index 00000000..c7655b1b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/DenyResponse.java @@ -0,0 +1,112 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.DenyReason; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Deny response + */ +public class DenyResponse extends OSPMessage +{ + private static final int ADDITIONAL_INFORMATION_FLAG = 16; + private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23}; + private static final int[] REASON = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ADDITIONAL_INFO = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DenyReason mDenyReason; + private String mAdditionalInfo; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public DenyResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" SERVICE:").append(getDeniedServiceType()); + sb.append(" REASON:").append(getDenyReason()); + + if(hasAdditionalInformation()) + { + sb.append(" INFO:").append(getAdditionalInfo()); + } + + return sb.toString(); + } + + private boolean hasAdditionalInformation() + { + return getMessage().get(ADDITIONAL_INFORMATION_FLAG); + } + + public String getAdditionalInfo() + { + if(mAdditionalInfo == null) + { + mAdditionalInfo = getMessage().getHex(ADDITIONAL_INFO, 6); + } + + return mAdditionalInfo; + } + + /** + * Opcode representing the service type that is being acknowledged by the radio unit. + */ + public Opcode getDeniedServiceType() + { + return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.INBOUND, Vendor.STANDARD); + } + + public DenyReason getDenyReason() + { + if(mDenyReason == null) + { + mDenyReason = DenyReason.fromCode(getMessage().getInt(REASON)); + } + + return mDenyReason; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ExtendedFunctionCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ExtendedFunctionCommand.java new file mode 100644 index 00000000..53a408a3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ExtendedFunctionCommand.java @@ -0,0 +1,88 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.ExtendedFunction; + +import java.util.ArrayList; +import java.util.List; + +/** + * Extended function response. + */ +public class ExtendedFunctionCommand extends OSPMessage +{ + private static final int[] FUNCTION = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ARGUMENTS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79}; + + private ExtendedFunction mExtendedFunction; + private String mArguments; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public ExtendedFunctionCommand(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" FUNCTION:").append(getExtendedFunction()); + sb.append(" ARGUMENTS:").append(getArguments()); + return sb.toString(); + } + + public ExtendedFunction getExtendedFunction() + { + if(mExtendedFunction == null) + { + mExtendedFunction = ExtendedFunction.fromValue(getMessage().getInt(FUNCTION)); + } + + return mExtendedFunction; + } + + public String getArguments() + { + if(mArguments == null) + { + mArguments = getMessage().getHex(ARGUMENTS, 6).toUpperCase(); + } + + return mArguments; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdate.java new file mode 100644 index 00000000..79944b75 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdate.java @@ -0,0 +1,144 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Identifier Update - Frequency Band details + */ +public class FrequencyBandUpdate extends OSPMessage implements IFrequencyBand +{ + private static final int[] FREQUENCY_BAND_IDENTIFIER = {16, 17, 18, 19}; + private static final int[] BANDWIDTH = {20, 21, 22, 23, 24, 25, 26, 27, 28}; + private static final int TRANSMIT_OFFSET_SIGN = 29; + private static final int[] TRANSMIT_OFFSET = {30, 31, 32, 33, 34, 35, 36, 37}; + private static final int[] CHANNEL_SPACING = {38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BASE_FREQUENCY = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public FrequencyBandUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ID:").append(getIdentifier()); + sb.append(" OFFSET:").append(getTransmitOffset()); + sb.append(" SPACING:").append(getChannelSpacing()); + sb.append(" BASE:").append(getBaseFrequency()); + sb.append(" FDMA BW:").append(getBandwidth()); + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER); + } + + @Override + public long getChannelSpacing() + { + return getMessage().getInt(CHANNEL_SPACING) * 125; + } + + @Override + public long getBaseFrequency() + { + return getMessage().getLong(BASE_FREQUENCY) * 5; + } + + @Override + public int getBandwidth() + { + return getMessage().getInt(BANDWIDTH) * 125; + } + + @Override + public long getTransmitOffset() + { + long offset = getMessage().getLong(TRANSMIT_OFFSET) * 250000l; + + if(!getMessage().get(TRANSMIT_OFFSET_SIGN)) + { + offset *= -1; + } + + return offset; + } + + /** + * Indicates if the frequency band has a transmit option for the subscriber unit. + */ + public boolean hasTransmitOffset() + { + return getMessage().getInt(TRANSMIT_OFFSET) != 0x80; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (getChannelSpacing() * channelNumber); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + if(hasTransmitOffset()) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + return 0; + } + + @Override + public boolean isTDMA() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 1; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateTDMA.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateTDMA.java new file mode 100644 index 00000000..2b9ab429 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateTDMA.java @@ -0,0 +1,157 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.ChannelType; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Identifier Update - Frequency Band details - TDMA bands + */ +public class FrequencyBandUpdateTDMA extends OSPMessage implements IFrequencyBand +{ + private static final int[] FREQUENCY_BAND_IDENTIFIER = {16, 17, 18, 19}; + private static final int[] CHANNEL_TYPE = {20, 21, 22, 23}; + private static final int TRANSMIT_OFFSET_SIGN = 24; + private static final int[] TRANSMIT_OFFSET = {25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37}; + private static final int[] CHANNEL_SPACING = {38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BASE_FREQUENCY = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private ChannelType mChannelType; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public FrequencyBandUpdateTDMA(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ID:").append(getIdentifier()); + sb.append(" OFFSET:").append(getTransmitOffset()); + sb.append(" SPACING:").append(getChannelSpacing()); + sb.append(" BASE:").append(getBaseFrequency()); + sb.append(" ").append(getChannelType()); + return sb.toString(); + } + + public ChannelType getChannelType() + { + if(mChannelType == null) + { + mChannelType = ChannelType.fromValue(getMessage().getInt(CHANNEL_TYPE)); + } + + return mChannelType; + } + + @Override + public int getIdentifier() + { + return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER); + } + + @Override + public long getChannelSpacing() + { + return getMessage().getInt(CHANNEL_SPACING) * 125; + } + + @Override + public long getBaseFrequency() + { + return getMessage().getLong(BASE_FREQUENCY) * 5; + } + + @Override + public int getBandwidth() + { + return getChannelType().getBandwidth(); + } + + @Override + public long getTransmitOffset() + { + long offset = getMessage().getLong(TRANSMIT_OFFSET) * 250000l; + + if(!getMessage().get(TRANSMIT_OFFSET_SIGN)) + { + offset *= -1; + } + + return offset; + } + + /** + * Indicates if the frequency band has a transmit option for the subscriber unit. + */ + public boolean hasTransmitOffset() + { + return getMessage().getInt(TRANSMIT_OFFSET) != 0x80; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (getChannelSpacing() * channelNumber); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + if(hasTransmitOffset()) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + return 0; + } + + @Override + public boolean isTDMA() + { + return getChannelType().isTDMA(); + } + + @Override + public int getTimeslotCount() + { + return getChannelType().getSlotsPerCarrier(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateVUHF.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateVUHF.java new file mode 100644 index 00000000..380c79f8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/FrequencyBandUpdateVUHF.java @@ -0,0 +1,155 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Identifier Update - VHF/UHF Frequency Band details + */ +public class FrequencyBandUpdateVUHF extends OSPMessage implements IFrequencyBand +{ + private static final int[] FREQUENCY_BAND_IDENTIFIER = {16, 17, 18, 19}; + private static final int[] BANDWIDTH = {20, 21, 22, 23}; + private static final int TRANSMIT_OFFSET_SIGN = 24; + private static final int[] TRANSMIT_OFFSET = {25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37}; + private static final int[] CHANNEL_SPACING = {38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] BASE_FREQUENCY = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public FrequencyBandUpdateVUHF(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" ID:").append(getIdentifier()); + sb.append(" OFFSET:").append(getTransmitOffset()); + sb.append(" SPACING:").append(getChannelSpacing()); + sb.append(" BASE:").append(getBaseFrequency()); + sb.append(" FDMA BW:").append(getBandwidth()); + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER); + } + + @Override + public long getChannelSpacing() + { + return getMessage().getInt(CHANNEL_SPACING) * 125; + } + + @Override + public long getBaseFrequency() + { + return getMessage().getLong(BASE_FREQUENCY) * 5; + } + + @Override + public int getBandwidth() + { + int bandwidth = getMessage().getInt(BANDWIDTH); + + if(bandwidth == 0x4) + { + return 6250; + } + else if(bandwidth == 0x5) + { + return 12500; + } + + return 0; + } + + @Override + public long getTransmitOffset() + { + long offset = getMessage().getLong(TRANSMIT_OFFSET) * getChannelSpacing(); + + if(!getMessage().get(TRANSMIT_OFFSET_SIGN)) + { + offset *= -1; + } + + return offset; + } + + /** + * Indicates if the frequency band has a transmit option for the subscriber unit. + */ + public boolean hasTransmitOffset() + { + return getMessage().getInt(TRANSMIT_OFFSET) != 0x80; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (getChannelSpacing() * channelNumber); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + if(hasTransmitOffset()) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + return 0; + } + + @Override + public boolean isTDMA() + { + return false; + } + + @Override + public int getTimeslotCount() + { + return 1; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationQuery.java new file mode 100644 index 00000000..24e1d837 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationQuery.java @@ -0,0 +1,77 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group affiliation query. + */ +public class GroupAffiliationQuery extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupAffiliationQuery(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationResponse.java new file mode 100644 index 00000000..ca756703 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupAffiliationResponse.java @@ -0,0 +1,113 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnnouncementTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Response; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group affiliation response. + */ +public class GroupAffiliationResponse extends OSPMessage +{ + private static final int GLOBAL_LOCAL_FLAG = 16; + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESPONSE = {22, 23}; + private static final int[] ANNOUNCEMENT_GROUP_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Response mAffiliationResponse; + private Identifier mAnnouncementGroupAddress; + private Identifier mGroupAddress; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupAffiliationResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" GROUP ADDRESS:").append(getGroupAddress()); + sb.append(" (").append(isGlobalAffiliation() ? "GLOBAL" : "LOCAL").append(")"); + sb.append(" AFFILIATION:").append(getAffiliationResponse()); + sb.append(" ANNOUNCEMENT GROUP:").append(getAnnouncementGroupAddress()); + return sb.toString(); + } + + public boolean isGlobalAffiliation() + { + return getMessage().get(GLOBAL_LOCAL_FLAG); + } + + public Response getAffiliationResponse() + { + if(mAffiliationResponse == null) + { + mAffiliationResponse = Response.fromValue(getMessage().getInt(RESPONSE)); + } + + return mAffiliationResponse; + } + + public Identifier getAnnouncementGroupAddress() + { + if(mAnnouncementGroupAddress == null) + { + mAnnouncementGroupAddress = APCO25AnnouncementTalkgroup.create(getMessage().getInt(ANNOUNCEMENT_GROUP_ADDRESS)); + } + + return mAnnouncementGroupAddress; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25AnyTalkgroup.create(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getAnnouncementGroupAddress()); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncement.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncement.java new file mode 100644 index 00000000..19336ec3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncement.java @@ -0,0 +1,115 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice channel grant update. + */ +public class GroupDataChannelAnnouncement extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] FREQUENCY_BAND_A = {16, 17, 18, 19}; + private static final int[] CHANNEL_NUMBER_A = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] GROUP_ADDRESS_A = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] FREQUENCY_BAND_B = {48, 49, 50, 51}; + private static final int[] CHANNEL_NUMBER_B = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] GROUP_ADDRESS_B = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private IChannelDescriptor mChannelA; + private Identifier mGroupAddressA; + private IChannelDescriptor mChannelB; + private Identifier mGroupAddressB; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupDataChannelAnnouncement(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" GROUP A:").append(getGroupAddressA()); + sb.append(" CHAN:").append(getChannelA()); + sb.append(" GROUP B:").append(getGroupAddressB()); + sb.append(" CHAN:").append(getChannelB()); + return sb.toString(); + } + + public IChannelDescriptor getChannelA() + { + if(mChannelA == null) + { + mChannelA = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_A), + getMessage().getInt(CHANNEL_NUMBER_A)); + } + + return mChannelA; + } + + public Identifier getGroupAddressA() + { + if(mGroupAddressA == null) + { + mGroupAddressA = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS_A)); + } + + return mGroupAddressA; + } + + public IChannelDescriptor getChannelB() + { + if(mChannelB == null) + { + mChannelB = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_B), + getMessage().getInt(CHANNEL_NUMBER_B)); + } + + return mChannelB; + } + + public Identifier getGroupAddressB() + { + if(mGroupAddressB == null) + { + mGroupAddressB = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS_B)); + } + + return mGroupAddressB; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddressA()); + mIdentifiers.add(getGroupAddressB()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannelA()); + channels.add(getChannelB()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncementExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncementExplicit.java new file mode 100644 index 00000000..6e98638c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelAnnouncementExplicit.java @@ -0,0 +1,90 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice channel grant update. + */ +public class GroupDataChannelAnnouncementExplicit extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {32, 33, 34, 35}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UPLINK_FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] UPLINK_CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] GROUP_ADDRESS = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private IChannelDescriptor mChannel; + private Identifier mGroupAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupDataChannelAnnouncementExplicit(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" GROUP:").append(getGroupAddress()); + sb.append(" CHAN:").append(getChannel()); + return sb.toString(); + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelGrant.java new file mode 100644 index 00000000..cb3c0047 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupDataChannelGrant.java @@ -0,0 +1,137 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice call channel grant. + */ +public class GroupDataChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] FREQUENCY_BAND = {24, 25, 26, 27}; + private static final int[] CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mDataServiceOptions; + private APCO25Channel mChannel; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupDataChannelGrant(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" CHAN:").append(getChannel()); + sb.append(" ").append(getDataServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public DataServiceOptions getDataServiceOptions() + { + if(mDataServiceOptions == null) + { + mDataServiceOptions = new DataServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mDataServiceOptions; + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrant.java new file mode 100644 index 00000000..094f10ac --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrant.java @@ -0,0 +1,117 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice call channel grant. + */ +public class GroupVoiceChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] FREQUENCY_BAND = {24, 25, 26, 27}; + private static final int[] CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ADDRESS = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mGroupAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupVoiceChannelGrant(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getGroupAddress()); + sb.append(" CHAN:").append(getChannel()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdate.java new file mode 100644 index 00000000..e27a3814 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdate.java @@ -0,0 +1,127 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice channel grant update. + */ +public class GroupVoiceChannelGrantUpdate extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] FREQUENCY_BAND_A = {16, 17, 18, 19}; + private static final int[] CHANNEL_NUMBER_A = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] GROUP_ADDRESS_A = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] FREQUENCY_BAND_B = {48, 49, 50, 51}; + private static final int[] CHANNEL_NUMBER_B = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] GROUP_ADDRESS_B = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private APCO25Channel mChannelA; + private Identifier mGroupAddressA; + private APCO25Channel mChannelB; + private Identifier mGroupAddressB; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupVoiceChannelGrantUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" GROUP A:").append(getGroupAddressA()); + sb.append(" CHAN:").append(getChannelA()); + if(hasGroupB()) + { + sb.append(" GROUP B:").append(getGroupAddressB()); + sb.append(" CHAN:").append(getChannelB()); + } + return sb.toString(); + } + + public APCO25Channel getChannelA() + { + if(mChannelA == null) + { + mChannelA = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_A), + getMessage().getInt(CHANNEL_NUMBER_A)); + } + + return mChannelA; + } + + public Identifier getGroupAddressA() + { + if(mGroupAddressA == null) + { + mGroupAddressA = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS_A)); + } + + return mGroupAddressA; + } + + public boolean hasGroupB() + { + return (getMessage().getInt(GROUP_ADDRESS_A) != getMessage().getInt(GROUP_ADDRESS_B)) && + getMessage().getInt(GROUP_ADDRESS_B) != 0; + } + + public APCO25Channel getChannelB() + { + if(mChannelB == null) + { + mChannelB = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_B), + getMessage().getInt(CHANNEL_NUMBER_B)); + } + + return mChannelB; + } + + public Identifier getGroupAddressB() + { + if(mGroupAddressB == null) + { + mGroupAddressB = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS_B)); + } + + return mGroupAddressB; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddressA()); + if(hasGroupB()) + { + mIdentifiers.add(getGroupAddressB()); + } + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannelA()); + channels.add(getChannelB()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdateExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdateExplicit.java new file mode 100644 index 00000000..f0546f57 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/GroupVoiceChannelGrantUpdateExplicit.java @@ -0,0 +1,106 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Group voice call channel grant. + */ +public class GroupVoiceChannelGrantUpdateExplicit extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {32, 33, 34, 35}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UPLINK_FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] UPLINK_CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] GROUP_ADDRESS = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25ExplicitChannel mChannel; + private Identifier mGroupAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public GroupVoiceChannelGrantUpdateExplicit(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TALKGROUP:").append(getGroupAddress()); + sb.append(" CHAN:").append(getChannel()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public APCO25ExplicitChannel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/IndividualDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/IndividualDataChannelGrant.java new file mode 100644 index 00000000..8b8770dc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/IndividualDataChannelGrant.java @@ -0,0 +1,101 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Individual data channel grant. + */ +public class IndividualDataChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private IChannelDescriptor mChannel; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public IndividualDataChannelGrant(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" CHAN:").append(getChannel()); + return sb.toString(); + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/LocationRegistrationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/LocationRegistrationResponse.java new file mode 100644 index 00000000..7d97ff77 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/LocationRegistrationResponse.java @@ -0,0 +1,140 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Response; + +import java.util.ArrayList; +import java.util.List; + +/** + * Location registration response + */ +public class LocationRegistrationResponse extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21}; + private static final int[] RESPONSE = {22, 23}; + private static final int[] GROUP_ADDRESS = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] RFSS = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SITE = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Response mResponse; + private Identifier mRfss; + private Identifier mSite; + private Identifier mGroupAddress; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public LocationRegistrationResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" GROUP:").append(getGroupAddress()); + sb.append(" RFSS:").append(getRfss()); + sb.append(" SITE:").append(getSite()); + sb.append(" RESPONSE:").append(getResponse()); + return sb.toString(); + } + + public Response getResponse() + { + if(mResponse == null) + { + mResponse = Response.fromValue(getMessage().getInt(RESPONSE)); + } + + return mResponse; + } + + public Identifier getGroupAddress() + { + if(mGroupAddress == null) + { + mGroupAddress = APCO25AnyTalkgroup.create(getMessage().getInt(GROUP_ADDRESS)); + } + + return mGroupAddress; + } + + public Identifier getRfss() + { + if(mRfss == null) + { + mRfss = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRfss; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getGroupAddress()); + mIdentifiers.add(getRfss()); + mIdentifiers.add(getSite()); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/MessageUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/MessageUpdate.java new file mode 100644 index 00000000..40939e7c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/MessageUpdate.java @@ -0,0 +1,111 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.message.APCO25ShortDataMessage; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Message update + */ +public class MessageUpdate extends OSPMessage +{ + private static final int[] MESSAGE = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mShortDataMessage; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public MessageUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" SHORT DATA MESSAGE:").append(getShortDataMessage()); + return sb.toString(); + } + + public Identifier getShortDataMessage() + { + if(mShortDataMessage == null) + { + mShortDataMessage = APCO25ShortDataMessage.create(getMessage().getInt(MESSAGE)); + } + + return mShortDataMessage; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + mIdentifiers.add(getShortDataMessage()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/NetworkStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/NetworkStatusBroadcast.java new file mode 100644 index 00000000..d351bccc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/NetworkStatusBroadcast.java @@ -0,0 +1,148 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Network status broadcast + */ +public class NetworkStatusBroadcast extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] LOCATION_REGISTRATION_AREA = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] FREQUENCY_BAND = {56, 57, 58, 59}; + private static final int[] CHANNEL_NUMBER = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] SYSTEM_SERVICE_CLASS = {72, 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mLocationRegistrationArea; + private Identifier mWacn; + private Identifier mSystem; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + private List mIdentifiers; + private List mSiteFlags; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public NetworkStatusBroadcast(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" WACN:").append(getWacn()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + public Identifier getLocationRegistrationArea() + { + if(mLocationRegistrationArea == null) + { + mLocationRegistrationArea = APCO25Lra.create(getMessage().getInt(LOCATION_REGISTRATION_AREA)); + } + + return mLocationRegistrationArea; + } + + public Identifier getWacn() + { + if(mWacn == null) + { + mWacn = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWacn; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SYSTEM_SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getWacn()); + mIdentifiers.add(getSystem()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ProtectionParameterUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ProtectionParameterUpdate.java new file mode 100644 index 00000000..83cf9bc3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/ProtectionParameterUpdate.java @@ -0,0 +1,99 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.encryption.APCO25EncryptionKey; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Protection parameter (e.g. encryption) update + */ +public class ProtectionParameterUpdate extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ALGORITHM_ID = {32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] KEY_ID = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mEncryptionKey; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public ProtectionParameterUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ENCRYPTION KEY:").append(getEncryptionKey()); + return sb.toString(); + } + + public Identifier getEncryptionKey() + { + if(mEncryptionKey == null) + { + int algorithm = getMessage().getInt(ALGORITHM_ID); + int key = getMessage().getInt(KEY_ID); + mEncryptionKey = APCO25EncryptionKey.create(algorithm, key); + } + + return mEncryptionKey; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createGroup(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getEncryptionKey()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/QueuedResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/QueuedResponse.java new file mode 100644 index 00000000..c313896d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/QueuedResponse.java @@ -0,0 +1,132 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Direction; +import io.github.dsheirer.module.decode.p25.reference.QueuedResponseReason; +import io.github.dsheirer.module.decode.p25.reference.Vendor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Queued response + */ +public class QueuedResponse extends OSPMessage +{ + private static final int ADDITIONAL_INFORMATION_FLAG = 16; + private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23}; + private static final int[] REASON = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] ADDITIONAL_INFO = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private QueuedResponseReason mQueuedResponseReason; + private String mAdditionalInfo; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public QueuedResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" SERVICE:").append(getQueuedResponseServiceType()); + sb.append(" REASON:").append(getQueuedResponseReason()); + + if(hasAdditionalInformation()) + { + sb.append(" INFO:").append(getAdditionalInfo()); + } + + return sb.toString(); + } + + private boolean hasAdditionalInformation() + { + return getMessage().get(ADDITIONAL_INFORMATION_FLAG); + } + + public String getAdditionalInfo() + { + if(mAdditionalInfo == null) + { + mAdditionalInfo = getMessage().getHex(ADDITIONAL_INFO, 6); + } + + return mAdditionalInfo; + } + + /** + * Opcode representing the service type that is being acknowledged by the radio unit. + */ + public Opcode getQueuedResponseServiceType() + { + return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.OUTBOUND, Vendor.STANDARD); + } + + public QueuedResponseReason getQueuedResponseReason() + { + if(mQueuedResponseReason == null) + { + mQueuedResponseReason = QueuedResponseReason.fromCode(getMessage().getInt(REASON)); + } + + return mQueuedResponseReason; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RFSSStatusBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RFSSStatusBroadcast.java new file mode 100644 index 00000000..de3ab634 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RFSSStatusBroadcast.java @@ -0,0 +1,175 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Lra; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * RF Sub-System status broadcast + */ +public class RFSSStatusBroadcast extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] LOCATION_REGISTRATION_AREA = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int ACTIVE_NETWORK_CONNECTION_TO_RFSS_CONTROLLER_FLAG = 27; + private static final int[] SYSTEM = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] RFSS = {40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SITE = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] FREQUENCY_BAND = {56, 57, 58, 59}; + private static final int[] CHANNEL_NUMBER = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] SYSTEM_SERVICE_CLASS = {72, 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mLocationRegistrationArea; + private Identifier mSystem; + private Identifier mSite; + private Identifier mRfss; + private IChannelDescriptor mChannel; + private VoiceServiceOptions mVoiceServiceOptions; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public RFSSStatusBroadcast(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" LRA:").append(getLocationRegistrationArea()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" RFSS:").append(getRfss()); + sb.append(" SITE:").append(getSite()); + if(isActiveNetworkConnectionToRfssControllerSite()) + { + sb.append(" ACTIVE NETWORK CONNECTION"); + } + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptions()); + return sb.toString(); + } + + /** + * Indicates if the site has an active network connection to the RFSS controller + */ + public boolean isActiveNetworkConnectionToRfssControllerSite() + { + return getMessage().get(ACTIVE_NETWORK_CONNECTION_TO_RFSS_CONTROLLER_FLAG); + } + + public Identifier getLocationRegistrationArea() + { + if(mLocationRegistrationArea == null) + { + mLocationRegistrationArea = APCO25Lra.create(getMessage().getInt(LOCATION_REGISTRATION_AREA)); + } + + return mLocationRegistrationArea; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public Identifier getRfss() + { + if(mRfss == null) + { + mRfss = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRfss; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SYSTEM_SERVICE_CLASS)); + } + + return mVoiceServiceOptions; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getLocationRegistrationArea()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getSite()); + mIdentifiers.add(getRfss()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RadioUnitMonitorCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RadioUnitMonitorCommand.java new file mode 100644 index 00000000..3794dcc9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RadioUnitMonitorCommand.java @@ -0,0 +1,108 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Radio unit monitor command + */ +public class RadioUnitMonitorCommand extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; + private static final int[] TX_MULTIPLIER = {30, 31}; + private static final int[] SOURCE_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mSourceAddress; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public RadioUnitMonitorCommand(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" TX MULTIPLIER:").append(getTxMultiplier()); + return sb.toString(); + } + + /** + * Multipler for a value stored in the radio that determines how long the target address radio will be + * keyed for monitoring. + */ + public int getTxMultiplier() + { + return getMessage().getInt(TX_MULTIPLIER); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RoamingAddressCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RoamingAddressCommand.java new file mode 100644 index 00000000..f187cda4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/RoamingAddressCommand.java @@ -0,0 +1,124 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.ISPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.StackOperation; + +import java.util.ArrayList; +import java.util.List; + +/** + * Roaming address command + */ +public class RoamingAddressCommand extends ISPMessage +{ + private static final int[] STACK_OPERATION = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private StackOperation mStackOperation; + private Identifier mWACN; + private Identifier mSystem; + private Identifier mTargetId; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public RoamingAddressCommand(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetId()); + sb.append(" STACK OPERATION:").append(getStackOperation()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + return sb.toString(); + } + + public StackOperation getStackOperation() + { + if(mStackOperation == null) + { + mStackOperation = StackOperation.fromValue(getMessage().getInt(STACK_OPERATION)); + } + + return mStackOperation; + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetId() + { + if(mTargetId == null) + { + mTargetId = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ID)); + } + + return mTargetId; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getTargetId()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelAnnouncementExplicit.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelAnnouncementExplicit.java new file mode 100644 index 00000000..0400ad54 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelAnnouncementExplicit.java @@ -0,0 +1,151 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Sub-Network Dependent Convergence Protocol (SNDCP) Data Channel Announcement - Explicit + */ +public class SNDCPDataChannelAnnouncementExplicit extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] DATA_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int AUTONOMOUS_ACCESS_FLAG = 24; + private static final int REQUESTED_ACCESS_FLAG = 25; + private static final int[] DOWNLINK_FREQUENCY_BAND = {32, 33, 34, 35}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UPLINK_FREQUENCY_BAND = {48, 49, 50, 51}; + private static final int[] UPLINK_CHANNEL_NUMBER = {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] DATA_ACCESS_CONTROL = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mServiceOptions; + private IChannelDescriptor mChannel; + private List mChannels; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SNDCPDataChannelAnnouncementExplicit(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" CHAN:").append(getChannel()); + if(isAutonomousAccess() && isRequestedAccess()) + { + sb.append(" AUTONOMOUS/REQUESTED-ACCESS"); + } + else if(isAutonomousAccess()) + { + sb.append(" AUTONOMOUS-ACCESS"); + } + else if(isRequestedAccess()) + { + sb.append(" REQUESTED-ACCESS"); + } + sb.append(" DAC:").append(getDataAccessControl()); + sb.append(" **:").append(getMessage().getInt(DATA_SERVICE_OPTIONS)); + sb.append(" SERVICE OPTIONS:").append(getServiceOptions()); + return sb.toString(); + } + + public boolean isAutonomousAccess() + { + return getMessage().get(AUTONOMOUS_ACCESS_FLAG); + } + + public boolean isRequestedAccess() + { + return getMessage().get(REQUESTED_ACCESS_FLAG); + } + + public DataServiceOptions getServiceOptions() + { + if(mServiceOptions == null) + { + mServiceOptions = new DataServiceOptions(getMessage().getInt(DATA_SERVICE_OPTIONS)); + } + + return mServiceOptions; + } + + public IChannelDescriptor getChannel() + { + if(mChannel == null) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + + return mChannel; + } + + /** + * Indicates if the channel is an explicit channel, meaning that there are separate downlink + * and uplink channel numbers included. When false, the channel number is the same for both + * uplink and downlink. + * + * @return true if this is an explicit channel. + */ + private boolean isExplicitChannel() + { + return getMessage().getInt(UPLINK_CHANNEL_NUMBER) != 4095; + } + + public int getDataAccessControl() + { + return getMessage().getInt(DATA_ACCESS_CONTROL); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + mChannels.add(getChannel()); + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelGrant.java new file mode 100644 index 00000000..c24bc08b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataChannelGrant.java @@ -0,0 +1,148 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25ExplicitChannel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sub-Network Dependent Convergence Protocol (SNDCP) Data Channel Grant + */ +public class SNDCPDataChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] DATA_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] DOWNLINK_FREQUENCY_BAND = {24, 25, 26, 27}; + private static final int[] DOWNLINK_CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] UPLINK_FREQUENCY_BAND = {40, 41, 42, 43}; + private static final int[] UPLINK_CHANNEL_NUMBER = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mServiceOptions; + private APCO25Channel mChannel; + private Identifier mTargetAddress; + private List mIdentifiers; + private List mChannels; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SNDCPDataChannelGrant(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" CHAN:").append(getChannel()); + sb.append(" SERVICE OPTIONS:").append(getServiceOptions()); + return sb.toString(); + } + + public DataServiceOptions getServiceOptions() + { + if(mServiceOptions == null) + { + mServiceOptions = new DataServiceOptions(getMessage().getInt(DATA_SERVICE_OPTIONS)); + } + + return mServiceOptions; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + if(isExplicitChannel()) + { + mChannel = APCO25ExplicitChannel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER), getMessage().getInt(UPLINK_FREQUENCY_BAND), + getMessage().getInt(UPLINK_CHANNEL_NUMBER)); + } + else + { + mChannel = APCO25Channel.create(getMessage().getInt(DOWNLINK_FREQUENCY_BAND), + getMessage().getInt(DOWNLINK_CHANNEL_NUMBER)); + } + } + + return mChannel; + } + + /** + * Indicates if the channel is an explicit channel, meaning that there are separate downlink + * and uplink channel numbers included. When false, the channel number is the same for both + * uplink and downlink. + * + * @return true if this is an explicit channel. + */ + private boolean isExplicitChannel() + { + return getMessage().getInt(UPLINK_CHANNEL_NUMBER) != 4095; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + if(mChannels == null) + { + mChannels = new ArrayList<>(); + mChannels.add(getChannel()); + } + + return mChannels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataPageRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataPageRequest.java new file mode 100644 index 00000000..5544926d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SNDCPDataPageRequest.java @@ -0,0 +1,114 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataServiceOptions; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sub-Network Dependent Convergence Protocol (SNDCP) Data Channel Grant + */ +public class SNDCPDataPageRequest extends OSPMessage +{ + private static final int[] DATA_SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private DataServiceOptions mServiceOptions; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SNDCPDataPageRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ").append(getServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public DataServiceOptions getServiceOptions() + { + if(mServiceOptions == null) + { + mServiceOptions = new DataServiceOptions(getMessage().getInt(DATA_SERVICE_OPTIONS)); + } + + return mServiceOptions; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SecondaryControlChannelBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SecondaryControlChannelBroadcast.java new file mode 100644 index 00000000..002ba6d1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SecondaryControlChannelBroadcast.java @@ -0,0 +1,174 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Rfss; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Site; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Secondary control channel broadcast + */ +public class SecondaryControlChannelBroadcast extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] RFSS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] SITE = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] FREQUENCY_BAND_A = {32, 33, 34, 35}; + private static final int[] CHANNEL_NUMBER_A = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SYSTEM_SERVICE_CLASS_A = {48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] FREQUENCY_BAND_B = {56, 57, 58, 59}; + private static final int[] CHANNEL_NUMBER_B = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + private static final int[] SYSTEM_SERVICE_CLASS_B = {72, 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mRfss; + private Identifier mSite; + private IChannelDescriptor mChannelA; + private IChannelDescriptor mChannelB; + private VoiceServiceOptions mVoiceServiceOptionsA; + private VoiceServiceOptions mVoiceServiceOptionsB; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SecondaryControlChannelBroadcast(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" RFSS:").append(getRfss()); + sb.append(" SITE:").append(getSite()); + sb.append(" CHAN A:").append(getChannelA()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptionsA()); + if(hasChannelB()) + { + sb.append(" CHAN B:").append(getChannelB()); + sb.append(" SERVICE OPTIONS:").append(getVoiceServiceOptionsB()); + } + return sb.toString(); + } + + public Identifier getRfss() + { + if(mRfss == null) + { + mRfss = APCO25Rfss.create(getMessage().getInt(RFSS)); + } + + return mRfss; + } + + public Identifier getSite() + { + if(mSite == null) + { + mSite = APCO25Site.create(getMessage().getInt(SITE)); + } + + return mSite; + } + + public IChannelDescriptor getChannelA() + { + if(mChannelA == null) + { + mChannelA = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_A), getMessage().getInt(CHANNEL_NUMBER_A)); + } + + return mChannelA; + } + + public VoiceServiceOptions getVoiceServiceOptionsA() + { + if(mVoiceServiceOptionsA == null) + { + mVoiceServiceOptionsA = new VoiceServiceOptions(getMessage().getInt(SYSTEM_SERVICE_CLASS_A)); + } + + return mVoiceServiceOptionsA; + } + + private boolean hasChannelB() + { + return getMessage().getInt(CHANNEL_NUMBER_A) != getMessage().getInt(CHANNEL_NUMBER_B) && + getMessage().getInt(SYSTEM_SERVICE_CLASS_B) != 0; + } + + public IChannelDescriptor getChannelB() + { + if(hasChannelB() && mChannelB == null) + { + mChannelB = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND_B), getMessage().getInt(CHANNEL_NUMBER_B)); + } + + return mChannelB; + } + + public VoiceServiceOptions getVoiceServiceOptionsB() + { + if(mVoiceServiceOptionsB == null) + { + mVoiceServiceOptionsB = new VoiceServiceOptions(getMessage().getInt(SYSTEM_SERVICE_CLASS_B)); + } + + return mVoiceServiceOptionsB; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSite()); + mIdentifiers.add(getRfss()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannelA()); + + if(hasChannelB()) + { + channels.add(getChannelB()); + } + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusQuery.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusQuery.java new file mode 100644 index 00000000..98bd9454 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusQuery.java @@ -0,0 +1,97 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status query + */ +public class StatusQuery extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public StatusQuery(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusUpdate.java new file mode 100644 index 00000000..da46b9ff --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/StatusUpdate.java @@ -0,0 +1,125 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.status.APCO25Status; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Status update + */ +public class StatusUpdate extends OSPMessage +{ + private static final int[] UNIT_STATUS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] USER_STATUS = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mUnitStatus; + private Identifier mUserStatus; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public StatusUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" UNIT STATUS:").append(getUnitStatus()); + sb.append(" USER STATUS:").append(getUserStatus()); + return sb.toString(); + } + + public Identifier getUnitStatus() + { + if(mUnitStatus == null) + { + mUnitStatus = APCO25Status.createUnitStatus(getMessage().getInt(UNIT_STATUS)); + } + + return mUnitStatus; + } + + public Identifier getUserStatus() + { + if(mUserStatus == null) + { + mUserStatus = APCO25Status.createUserStatus(getMessage().getInt(USER_STATUS)); + } + + return mUserStatus; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getUnitStatus()); + mIdentifiers.add(getUserStatus()); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SyncBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SyncBroadcast.java new file mode 100644 index 00000000..891b6c4e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SyncBroadcast.java @@ -0,0 +1,272 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +/** + * TDMA sync broadcast + */ +public class SyncBroadcast extends OSPMessage +{ + public static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28}; + public static final int SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE_FLAG = 29; + public static final int MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED_FLAG = 30; + public static final int[] LEAP_SECOND_CORRECTION = {31, 32}; + public static final int LOCAL_TIME_OFFSET_VALID_FLAG = 33; + public static final int LOCAL_TIME_OFFSET_SIGN = 34; + public static final int[] LOCAL_TIME_OFFSET_HOURS = {35, 36, 37, 38}; + public static final int LOCAL_TIME_OFFSET_HALF_HOUR = 39; + public static final int[] YEAR = {40, 41, 42, 43, 44, 45, 46}; + public static final int[] MONTH = {47, 48, 49, 50}; + public static final int[] DAY = {51, 52, 53, 54, 55}; + public static final int[] HOURS = {56, 57, 58, 59, 60}; + public static final int[] MINUTES = {61, 62, 63, 65, 65, 66}; + public static final int[] MICRO_SLOTS = {67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + + private static final DateFormat TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z"); + private static final TimeZone NO_TIME_ZONE = new SimpleTimeZone(0, "NONE"); + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SyncBroadcast(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" SYSTEM TIME"); + if(isSystemTimeNotLockedToExternalReference()) + { + sb.append(" UNLOCKED"); + } + sb.append(":"); + TIME_FORMATTER.setTimeZone(getTimeZone()); + sb.append(" " + TIME_FORMATTER.format(new Date(getSystemTime()))); + sb.append(" LEAP-SECOND CORRECTION:" + getLeapSecondCorrection() + "mS"); + if(isMicroslotsLockedToMinuteRollover()) + { + sb.append(" MICROSLOT-MINUTE ROLLOVER:SLOW"); + } + else + { + sb.append(" MICROSLOT-MINUTE ROLLOVER:UNLOCKED"); + } + return sb.toString(); + } + + /** + * System Time (UTC) in milliseconds since java epoch + */ + public long getSystemTime() + { + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal.clear(); + cal.set(Calendar.YEAR, getYear()); + cal.set(Calendar.MONTH, getMonth() - 1); + cal.set(Calendar.DAY_OF_MONTH, getDay()); + cal.set(Calendar.HOUR_OF_DAY, getHours()); + cal.set(Calendar.MINUTE, getMinutes()); + cal.set(Calendar.MILLISECOND, getMilliSeconds()); + return cal.getTimeInMillis(); + } + + /** + * System Time not locked to external time reference indicator + */ + public boolean isSystemTimeNotLockedToExternalReference() + { + return getMessage().get(SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE_FLAG); + } + + /** + * Micro-slot-to-Minute rollover boundary lock indicator. + * + * Note: we invert the message value so that true indicates that the micro + * slots and minute rollovers are locked, whereas the ICD uses false to + * indicate a lock. + * + * @return false if the microslots and minutes rollover are not locked. This + * indicates that the microslots minute rollover is free rolling. + */ + public boolean isMicroslotsLockedToMinuteRollover() + { + return !getMessage().get(MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED_FLAG); + } + + /** + * Leap second correction value that should be applied to the system time. + * + * The leap-second correction field indicates the number of 2.5 millisecond + * units that must be added to the system time to account for the insertion + * of leap seconds when the system time is represented as UTC universal time. + * + * @return leap-second correction value in milliseconds 0.0-10.0 + */ + public double getLeapSecondCorrection() + { + return (double)getMessage().getInt(LEAP_SECOND_CORRECTION) * 2.5d; + } + + + /** + * Indicates the local time offset fields contain valid information. + */ + public boolean isValidLocalTimeOffset() + { + return !getMessage().get(LOCAL_TIME_OFFSET_VALID_FLAG); + } + + /** + * Local time zone offset from UTC universal time (zulu) when the VALID + * LOCAL TIME OFFSET flag indicates a valid offset. Otherwise, this method + * returns a static +00:00 indicating no local time offset. + */ + public TimeZone getTimeZone() + { + if(isValidLocalTimeOffset()) + { + int offset = 0; + + offset += getMessage().getInt(LOCAL_TIME_OFFSET_HOURS) * 3600000; + offset += getMessage().get(LOCAL_TIME_OFFSET_HALF_HOUR) ? 1800000 : 0; + offset = getMessage().get(LOCAL_TIME_OFFSET_SIGN) ? -offset : offset; + + return new SimpleTimeZone(offset, "LOCAL"); + } + else + { + return NO_TIME_ZONE; + } + } + + /** + * System time - year. + * + * @return year in range 2000-2127 + */ + public int getYear() + { + return 2000 + getMessage().getInt(YEAR); + } + + /** + * System time - month + * + * @return month 1-12 where 1=January and 12=December + */ + public int getMonth() + { + return getMessage().getInt(MONTH); + } + + /** + * System time - day of month + * + * @return day of month in range 1-31 + */ + public int getDay() + { + return getMessage().getInt(DAY); + } + + /** + * System time - hours + * + * @return hours in range 0 - 23 + */ + public int getHours() + { + return getMessage().getInt(HOURS); + } + + /** + * System time - minutes + * + * @return minutes in range 0 - 59 + */ + public int getMinutes() + { + return getMessage().getInt(MINUTES); + } + + /** + * System time - milli-seconds + * + * Note: sub-millisecond values are rounded to nearest millisecond unit to + * conform to java Date internal milli-second precision level. + * + * @return milli-seconds in range 0 - 59999; + */ + public int getMilliSeconds() + { + return (int)((double)getMicroSlots() * 7.5); + } + + /** + * TDMA Micro Slots. + * + * Number of 7.5 millisecond micro slots since the last minute rollover, or + * since the last micro-slot counter rollover when the the micro-slot to + * minute rollover is unlocked. + * + * @return number of 7.5 mS micro-slots since last minute or micro-slot + * rollover + * @see isMicroslotsLockedToMinuteRollover() method. + * + * This value supports mobile subscriber timing alignment with a TDMA traffic + * channel when a channel is granted from the control channels, so that when + * the mobile changes from the control channel to the traffic channel, it is + * already aligned with the traffic channel TDMA timing. + * + * Super frames occur every 360 ms (48 micro-slots) on FDMA and Ultra frames + * occur every 4 super frames on TDMA. + * + * Valid micro-slot values range 0-7999 and represent 60,000 + * milliseconds ( 8000 x 7.5 ms). + */ + public int getMicroSlots() + { + return getMessage().getInt(MICRO_SLOTS); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SystemServiceBroadcast.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SystemServiceBroadcast.java new file mode 100644 index 00000000..41976288 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/SystemServiceBroadcast.java @@ -0,0 +1,90 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Service; + +import java.util.Collections; +import java.util.List; + +/** + * Secondary control channel broadcast + */ +public class SystemServiceBroadcast extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] AVAILABLE_SERVICES = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SUPPORTED_SERVICES = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71}; + private static final int[] REQUEST_PRIORITY_LEVEL = {72, 73, 74, 75, 76, 77, 78, 79}; + + private List mAvailableServices; + private List mSupportedServices; + + + /** + * Constructs a TSBK from the binary message sequence. + */ + public SystemServiceBroadcast(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" AVAILABLE SERVICES ").append(getAvailableServices()); + sb.append(" SUPPORTED SERVICES ").append(getSupportedServices()); + return sb.toString(); + } + + public List getAvailableServices() + { + if(mAvailableServices == null) + { + mAvailableServices = Service.getServices(getMessage().getInt(AVAILABLE_SERVICES)); + } + + return mAvailableServices; + } + + public List getSupportedServices() + { + if(mSupportedServices == null) + { + mSupportedServices = Service.getServices(getMessage().getInt(SUPPORTED_SERVICES)); + } + + return mSupportedServices; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectAnswerRequest.java new file mode 100644 index 00000000..2863ada7 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectAnswerRequest.java @@ -0,0 +1,97 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.telephone.APCO25TelephoneNumber; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Digit; + +import java.util.ArrayList; +import java.util.List; + +/** + * Telephone Interconnect Answer Request. + */ +public class TelephoneInterconnectAnswerRequest extends OSPMessage +{ + private static final int[] DIGIT_1 = {16, 17, 18, 19}; + private static final int[] DIGIT_2 = {20, 21, 22, 23}; + private static final int[] DIGIT_3 = {24, 25, 26, 27}; + private static final int[] DIGIT_4 = {28, 29, 30, 31}; + private static final int[] DIGIT_5 = {32, 33, 34, 35}; + private static final int[] DIGIT_6 = {36, 37, 38, 39}; + private static final int[] DIGIT_7 = {40, 41, 42, 43}; + private static final int[] DIGIT_8 = {44, 45, 46, 47}; + private static final int[] DIGIT_9 = {48, 49, 50, 51}; + private static final int[] DIGIT_10 = {52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79}; + + private Identifier mTelephoneNumber; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public TelephoneInterconnectAnswerRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getTelephoneNumber()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTelephoneNumber() + { + if(mTelephoneNumber == null) + { + List digits = new ArrayList<>(); + digits.add(getMessage().getInt(DIGIT_1)); + digits.add(getMessage().getInt(DIGIT_2)); + digits.add(getMessage().getInt(DIGIT_3)); + digits.add(getMessage().getInt(DIGIT_4)); + digits.add(getMessage().getInt(DIGIT_5)); + digits.add(getMessage().getInt(DIGIT_6)); + digits.add(getMessage().getInt(DIGIT_7)); + digits.add(getMessage().getInt(DIGIT_8)); + digits.add(getMessage().getInt(DIGIT_9)); + digits.add(getMessage().getInt(DIGIT_10)); + + mTelephoneNumber = APCO25TelephoneNumber.createFrom(Digit.decode(digits)); + } + + return mTelephoneNumber; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getTelephoneNumber()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrant.java new file mode 100644 index 00000000..e36e3628 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrant.java @@ -0,0 +1,112 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Telephone interconnect voice call channel grant. + */ +public class TelephoneInterconnectVoiceChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] FREQUENCY_BAND = {24, 25, 26, 27}; + private static final int[] CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] CALL_TIMER = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public TelephoneInterconnectVoiceChannelGrant(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" RADIO:").append(getSourceAddress()); + sb.append(" TIMER:").append(getCallTimer()); + sb.append("ms CHAN:").append(getChannel()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Call duration timer in milliseconds + */ + public long getCallTimer() + { + return getMessage().getInt(CALL_TIMER) * 100l; //Convert from 100ms units to milliseconds + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrantUpdate.java new file mode 100644 index 00000000..611d1be0 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/TelephoneInterconnectVoiceChannelGrantUpdate.java @@ -0,0 +1,112 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25AnyTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Telephone interconnect voice call channel grant. + */ +public class TelephoneInterconnectVoiceChannelGrantUpdate extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] FREQUENCY_BAND = {24, 25, 26, 27}; + private static final int[] CHANNEL_NUMBER = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] CALL_TIMER = {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] ANY_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private APCO25Channel mChannel; + private Identifier mAnyAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public TelephoneInterconnectVoiceChannelGrantUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" RADIO:").append(getAnyAddress()); + sb.append(" TIMER:").append(getCallTimer()); + sb.append("ms CHAN:").append(getChannel()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + /** + * Call duration timer in milliseconds + */ + public long getCallTimer() + { + return getMessage().getInt(CALL_TIMER) * 100l; //Convert from 100ms units to milliseconds + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getAnyAddress() + { + if(mAnyAddress == null) + { + mAnyAddress = APCO25AnyTalkgroup.create(getMessage().getInt(ANY_ADDRESS)); + } + + return mAnyAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getAnyAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitDeRegistrationAcknowledge.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitDeRegistrationAcknowledge.java new file mode 100644 index 00000000..51d5819c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitDeRegistrationAcknowledge.java @@ -0,0 +1,111 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit de-registration acknowledge + */ +public class UnitDeRegistrationAcknowledge extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] WACN = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + private static final int[] SYSTEM = {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ID = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mWACN; + private Identifier mSystem; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitDeRegistrationAcknowledge(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" WACN:").append(getWACN()); + sb.append(" SYSTEM:").append(getSystem()); + return sb.toString(); + } + + public Identifier getWACN() + { + if(mWACN == null) + { + mWACN = APCO25Wacn.create(getMessage().getInt(WACN)); + } + + return mWACN; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM)); + } + + return mSystem; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ID)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getWACN()); + mIdentifiers.add(getSystem()); + mIdentifiers.add(getTargetAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationCommand.java new file mode 100644 index 00000000..c1eecc35 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationCommand.java @@ -0,0 +1,97 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit registration command + */ +public class UnitRegistrationCommand extends OSPMessage +{ + private static final int[] RESERVED = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitRegistrationCommand(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + return sb.toString(); + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationResponse.java new file mode 100644 index 00000000..930d7525 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitRegistrationResponse.java @@ -0,0 +1,126 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.APCO25System; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25IdentifierTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.Response; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit registration response + */ +public class UnitRegistrationResponse extends OSPMessage +{ + private static final int[] RESERVED = {16, 17}; + private static final int[] RESPONSE = {18, 19}; + private static final int[] SYSTEM_ID = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_UNIQUE_ID = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private Response mResponse; + private Identifier mSystem; + private Identifier mTargetUniqueId; + private Identifier mTargetAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitRegistrationResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" TO ADDR:").append(getTargetAddress()); + sb.append(" TO ID:").append(getTargetUniqueId()); + sb.append(" SYSTEM:").append(getSystem()); + sb.append(" RESPONSE:").append(getResponse()); + return sb.toString(); + } + + public Response getResponse() + { + if(mResponse == null) + { + mResponse = Response.fromValue(getMessage().getInt(RESPONSE)); + } + + return mResponse; + } + + public Identifier getSystem() + { + if(mSystem == null) + { + mSystem = APCO25System.create(getMessage().getInt(SYSTEM_ID)); + } + + return mSystem; + } + + public Identifier getTargetUniqueId() + { + if(mTargetUniqueId == null) + { + mTargetUniqueId = APCO25IdentifierTalkgroup.createIndividual(getMessage().getInt(TARGET_UNIQUE_ID)); + } + + return mTargetUniqueId; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetUniqueId()); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSystem()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitAnswerRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitAnswerRequest.java new file mode 100644 index 00000000..a68e2d75 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitAnswerRequest.java @@ -0,0 +1,94 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit to Unit answer request + */ +public class UnitToUnitAnswerRequest extends OSPMessage +{ + private static final int[] SERVICE_OPTIONS = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] RESERVED = {24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private VoiceServiceOptions mVoiceServiceOptions; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitToUnitAnswerRequest(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" ").append(getVoiceServiceOptions().toString()); + return sb.toString(); + } + + /** + * Service options for the request + */ + public VoiceServiceOptions getVoiceServiceOptions() + { + if(mVoiceServiceOptions == null) + { + mVoiceServiceOptions = new VoiceServiceOptions(getMessage().getInt(SERVICE_OPTIONS)); + } + + return mVoiceServiceOptions; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrant.java new file mode 100644 index 00000000..fad8531c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrant.java @@ -0,0 +1,101 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit to unit call channel grant. + */ +public class UnitToUnitVoiceChannelGrant extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private APCO25Channel mChannel; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitToUnitVoiceChannelGrant(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" CHAN:").append(getChannel()); + return sb.toString(); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrantUpdate.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrantUpdate.java new file mode 100644 index 00000000..27a1413f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnitToUnitVoiceChannelGrantUpdate.java @@ -0,0 +1,101 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25FromTalkgroup; +import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBandReceiver; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit to Unit voice channel grant update + */ +public class UnitToUnitVoiceChannelGrantUpdate extends OSPMessage implements IFrequencyBandReceiver +{ + private static final int[] FREQUENCY_BAND = {16, 17, 18, 19}; + private static final int[] CHANNEL_NUMBER = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] TARGET_ADDRESS = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55}; + private static final int[] SOURCE_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79}; + + private APCO25Channel mChannel; + private Identifier mTargetAddress; + private Identifier mSourceAddress; + private List mIdentifiers; + + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnitToUnitVoiceChannelGrantUpdate(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" FM:").append(getSourceAddress()); + sb.append(" TO:").append(getTargetAddress()); + sb.append(" CHAN:").append(getChannel()); + return sb.toString(); + } + + public APCO25Channel getChannel() + { + if(mChannel == null) + { + mChannel = APCO25Channel.create(getMessage().getInt(FREQUENCY_BAND), getMessage().getInt(CHANNEL_NUMBER)); + } + + return mChannel; + } + + public Identifier getTargetAddress() + { + if(mTargetAddress == null) + { + mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS)); + } + + return mTargetAddress; + } + + public Identifier getSourceAddress() + { + if(mSourceAddress == null) + { + mSourceAddress = APCO25FromTalkgroup.createIndividual(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mSourceAddress; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTargetAddress()); + mIdentifiers.add(getSourceAddress()); + } + + return mIdentifiers; + } + + @Override + public List getChannels() + { + List channels = new ArrayList<>(); + channels.add(getChannel()); + return channels; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnknownOSPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnknownOSPMessage.java new file mode 100644 index 00000000..aa2b2595 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/standard/osp/UnknownOSPMessage.java @@ -0,0 +1,38 @@ +package io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown/Unrecognized opcode message. + */ +public class UnknownOSPMessage extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnknownOSPMessage(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" **UNRECOGNIZED OSP OPCODE**"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/isp/UnknownVendorISPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/isp/UnknownVendorISPMessage.java new file mode 100644 index 00000000..f983c0f5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/isp/UnknownVendorISPMessage.java @@ -0,0 +1,59 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.unknown.isp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown/Unrecognized opcode message. + */ +public class UnknownVendorISPMessage extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnknownVendorISPMessage(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" VENDOR:").append(getVendor()); + sb.append(" UNRECOGNIZED ISP OPCODE"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/osp/UnknownVendorOSPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/osp/UnknownVendorOSPMessage.java new file mode 100644 index 00000000..a4a1e740 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/unknown/osp/UnknownVendorOSPMessage.java @@ -0,0 +1,59 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.message.tsbk.unknown.osp; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage; +import io.github.dsheirer.module.decode.p25.reference.DataUnitID; + +import java.util.Collections; +import java.util.List; + +/** + * Unknown/Unrecognized opcode message. + */ +public class UnknownVendorOSPMessage extends OSPMessage +{ + /** + * Constructs a TSBK from the binary message sequence. + */ + public UnknownVendorOSPMessage(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp) + { + super(dataUnitId, message, nac, timestamp); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" VENDOR:").append(getVendor()); + sb.append(" UNRECOGNIZED OSP OPCODE"); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/vendor/VendorOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/vendor/VendorOpcode.java deleted file mode 100644 index acfd0524..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/vendor/VendorOpcode.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.github.dsheirer.module.decode.p25.message.tsbk.vendor; - -public enum VendorOpcode -{ - OP00( "OPCODE 00 ", "VENDOR OPCODE 0x00", 0x00 ), - OP01( "OPCODE 01 ", "VENDOR OPCODE 0x01", 0x01 ), - OP02( "OPCODE 02 ", "VENDOR OPCODE 0x02", 0x02 ), - OP03( "OPCODE 03 ", "VENDOR OPCODE 0x03", 0x03 ), - OP04( "OPCODE 04 ", "VENDOR OPCODE 0x04", 0x04 ), - OP05( "OPCODE 05 ", "VENDOR OPCODE 0x05", 0x05 ), - OP06( "OPCODE 06 ", "VENDOR OPCODE 0x06", 0x06 ), - OP07( "OPCODE 07 ", "VENDOR OPCODE 0x07", 0x07 ), - OP08( "OPCODE 08 ", "VENDOR OPCODE 0x08", 0x08 ), - OP09( "OPCODE 09 ", "VENDOR OPCODE 0x09", 0x09 ), - OP0A( "OPCODE 0A ", "VENDOR OPCODE 0x0A", 0x0A ), - OP0B( "OPCODE 0B ", "VENDOR OPCODE 0x0B", 0x0B ), - OP0C( "OPCODE 0C ", "VENDOR OPCODE 0x0C", 0x0C ), - OP0D( "OPCODE 0D ", "VENDOR OPCODE 0x0D", 0x0D ), - OP0E( "OPCODE 0E ", "VENDOR OPCODE 0x0E", 0x0E ), - OP0F( "OPCODE 0F ", "VENDOR OPCODE 0x0F", 0x0F ), - OP10( "OPCODE 10 ", "VENDOR OPCODE 0x10", 0x10 ), - OP11( "OPCODE 11 ", "VENDOR OPCODE 0x11", 0x11 ), - OP12( "OPCODE 12 ", "VENDOR OPCODE 0x12", 0x12 ), - OP13( "OPCODE 13 ", "VENDOR OPCODE 0x13", 0x13 ), - OP14( "OPCODE 14 ", "VENDOR OPCODE 0x14", 0x14 ), - OP15( "OPCODE 15 ", "VENDOR OPCODE 0x15", 0x15 ), - OP16( "OPCODE 16 ", "VENDOR OPCODE 0x16", 0x16 ), - OP17( "OPCODE 17 ", "VENDOR OPCODE 0x17", 0x17 ), - OP18( "OPCODE 18 ", "VENDOR OPCODE 0x18", 0x18 ), - OP19( "OPCODE 19 ", "VENDOR OPCODE 0x19", 0x19 ), - OP1A( "OPCODE 1A ", "VENDOR OPCODE 0x1A", 0x1A ), - OP1B( "OPCODE 1B ", "VENDOR OPCODE 0x1B", 0x1B ), - OP1C( "OPCODE 1C ", "VENDOR OPCODE 0x1C", 0x1C ), - OP1D( "OPCODE 1D ", "VENDOR OPCODE 0x1D", 0x1D ), - OP1E( "OPCODE 1E ", "VENDOR OPCODE 0x1E", 0x1E ), - OP1F( "OPCODE 1F ", "VENDOR OPCODE 0x1F", 0x1F ), - OP20( "OPCODE 20 ", "VENDOR OPCODE 0x20", 0x20 ), - OP21( "OPCODE 21 ", "VENDOR OPCODE 0x21", 0x21 ), - OP22( "OPCODE 22 ", "VENDOR OPCODE 0x22", 0x22 ), - OP23( "OPCODE 23 ", "VENDOR OPCODE 0x23", 0x23 ), - OP24( "OPCODE 24 ", "VENDOR OPCODE 0x24", 0x24 ), - OP25( "OPCODE 25 ", "VENDOR OPCODE 0x25", 0x25 ), - OP26( "OPCODE 26 ", "VENDOR OPCODE 0x26", 0x26 ), - OP27( "OPCODE 27 ", "VENDOR OPCODE 0x27", 0x27 ), - OP28( "OPCODE 28 ", "VENDOR OPCODE 0x28", 0x28 ), - OP29( "OPCODE 29 ", "VENDOR OPCODE 0x29", 0x29 ), - OP2A( "OPCODE 2A ", "VENDOR OPCODE 0x2A", 0x2A ), - OP2B( "OPCODE 2B ", "VENDOR OPCODE 0x2B", 0x2B ), - OP2C( "OPCODE 2C ", "VENDOR OPCODE 0x2C", 0x2C ), - OP2D( "OPCODE 2D ", "VENDOR OPCODE 0x2D", 0x2D ), - OP2E( "OPCODE 2E ", "VENDOR OPCODE 0x2E", 0x2E ), - OP2F( "OPCODE 2F ", "VENDOR OPCODE 0x2F", 0x2F ), - OP30( "OPCODE 30 ", "VENDOR OPCODE 0x30", 0x30 ), - OP31( "OPCODE 31 ", "VENDOR OPCODE 0x31", 0x31 ), - OP32( "OPCODE 32 ", "VENDOR OPCODE 0x32", 0x32 ), - OP33( "OPCODE 33 ", "VENDOR OPCODE 0x33", 0x33 ), - OP34( "OPCODE 34 ", "VENDOR OPCODE 0x34", 0x34 ), - OP35( "OPCODE 35 ", "VENDOR OPCODE 0x35", 0x35 ), - OP36( "OPCODE 36 ", "VENDOR OPCODE 0x36", 0x36 ), - OP37( "OPCODE 37 ", "VENDOR OPCODE 0x37", 0x37 ), - OP38( "OPCODE 38 ", "VENDOR OPCODE 0x38", 0x38 ), - OP39( "OPCODE 39 ", "VENDOR OPCODE 0x39", 0x39 ), - OP3A( "OPCODE 3A ", "VENDOR OPCODE 0x3A", 0x3A ), - OP3B( "OPCODE 3B ", "VENDOR OPCODE 0x3B", 0x3B ), - OP3C( "OPCODE 3C ", "VENDOR OPCODE 0x3C", 0x3C ), - OP3D( "OPCODE 3D ", "VENDOR OPCODE 0x3D", 0x3D ), - OP3E( "OPCODE 3E ", "VENDOR OPCODE 0x3E", 0x3E ), - OP3F( "OPCODE 3F ", "VENDOR OPCODE 0x3F", 0x3F ), - UNKNOWN( "UNKNOWN ", "Unknown", -1 ); - - private String mLabel; - private String mDescription; - private int mCode; - - private VendorOpcode( String label, String description, int code ) - { - mLabel = label; - mDescription = description; - mCode = code; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } - - public String getDescription() - { - return mDescription; - } - - public int getCode() - { - return mCode; - } - - public static VendorOpcode fromValue( int value ) - { - if( 0 <= value && value <= 0x3F ) - { - return VendorOpcode.values()[ value ]; - } - - return UNKNOWN; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP1Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP1Message.java index 15af9627..ba876630 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP1Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP1Message.java @@ -1,42 +1,41 @@ package io.github.dsheirer.module.decode.p25.message.vselp; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.p25.message.P25Message; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import java.util.Collections; +import java.util.List; + public class VSELP1Message extends P25Message { - /** - * Motorola VSELP audio message 1. - * - * @param message - * @param duid - * @param aliasList - */ - public VSELP1Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - public String getMessageStub() + /** + * Motorola VSELP audio message 1 - not implemented. + */ + public VSELP1Message(CorrectedBinaryMessage message, int nac, long timestamp) { - StringBuilder sb = new StringBuilder(); + super(message, nac, timestamp); + } - sb.append( "VSELP 1 VOICE FRAME" ); + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } - return sb.toString(); + @Override + public DataUnitID getDUID() + { + return DataUnitID.VSELP1; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP2Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP2Message.java index 886d652a..6dd854cb 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP2Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/vselp/VSELP2Message.java @@ -1,42 +1,61 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.p25.message.vselp; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.p25.message.P25Message; import io.github.dsheirer.module.decode.p25.reference.DataUnitID; +import java.util.Collections; +import java.util.List; + public class VSELP2Message extends P25Message { - /** - * Motorola VSELP audio message 1. - * - * @param message - * @param duid - * @param aliasList - */ - public VSELP2Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - public String getMessageStub() + /** + * Motorola VSELP audio message - not implemented. + */ + public VSELP2Message(CorrectedBinaryMessage message, int nac, long timestamp) { - StringBuilder sb = new StringBuilder(); + super(message, nac, timestamp); + } - sb.append( "VSELP 2 VOICE FRAME" ); + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getMessageStub()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } - return sb.toString(); + @Override + public DataUnitID getDUID() + { + return DataUnitID.VSELP2; + } + + @Override + public List getIdentifiers() + { + return Collections.EMPTY_LIST; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/network/P25NetworkConfigurationMonitor.java b/src/main/java/io/github/dsheirer/module/decode/p25/network/P25NetworkConfigurationMonitor.java new file mode 100644 index 00000000..62325035 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/network/P25NetworkConfigurationMonitor.java @@ -0,0 +1,487 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.network; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.module.decode.p25.P25Decoder; +import io.github.dsheirer.module.decode.p25.message.IFrequencyBand; +import io.github.dsheirer.module.decode.p25.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCAdjacentSiteStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCAdjacentSiteStatusBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCNetworkStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCNetworkStatusBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCRFSSStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCRFSSStatusBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCSecondaryControlChannelBroadcast; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCSecondaryControlChannelBroadcastExplicit; +import io.github.dsheirer.module.decode.p25.message.lc.standard.LCSystemServiceBroadcast; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.message.pdu.ambtc.osp.AMBTCAdjacentStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.AdjacentStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.NetworkStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.RFSSStatusBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SNDCPDataChannelAnnouncementExplicit; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SecondaryControlChannelBroadcast; +import io.github.dsheirer.module.decode.p25.message.tsbk.standard.osp.SystemServiceBroadcast; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * Tracks the network configuration details of a P25 network from the broadcast messages + */ +public class P25NetworkConfigurationMonitor +{ + private final static Logger mLog = LoggerFactory.getLogger(P25NetworkConfigurationMonitor.class); + + private Map mFrequencyBandMap = new HashMap<>(); + + //Network Status Messages + private NetworkStatusBroadcast mTSBKNetworkStatusBroadcast; + private LCNetworkStatusBroadcast mLCNetworkStatusBroadcast; + private LCNetworkStatusBroadcastExplicit mLCNetworkStatusBroadcastExplicit; + + //Current Site Status Messagese + private RFSSStatusBroadcast mTSBKRFSSStatusBroadcast; + private LCRFSSStatusBroadcast mLCRFSSStatusBroadcast; + private LCRFSSStatusBroadcastExplicit mLCRFSSStatusBroadcastExplicit; + + //Current Site Secondary Control Channels + private Map mSecondaryControlChannels = new TreeMap<>(); + + //Current Site Data Channel + private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; + + //Current Site Services + private SystemServiceBroadcast mTSBKSystemServiceBroadcast; + private LCSystemServiceBroadcast mLCSystemServiceBroadcast; + + //Neighbor Sites + private Map mAMBTCNeighborSites = new HashMap<>(); + private Map mLCNeighborSites = new HashMap<>(); + private Map mLCNeighborSitesExplicit = new HashMap<>(); + private Map mTSBKNeighborSites = new HashMap<>(); + + private P25Decoder.Modulation mModulation; + + /** + * Constructs a network configuration monitor. + * + * @param modulation type used by the decoder + */ + public P25NetworkConfigurationMonitor(P25Decoder.Modulation modulation) + { + mModulation = modulation; + } + + /** + * Processes TSBK network configuration messages + */ + public void process(TSBKMessage tsbk) + { + switch(tsbk.getOpcode()) + { + case OSP_IDENTIFIER_UPDATE: + case OSP_IDENTIFIER_UPDATE_TDMA: + case OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS: + if(tsbk instanceof IFrequencyBand) + { + IFrequencyBand frequencyBand = (IFrequencyBand)tsbk; + mFrequencyBandMap.put(frequencyBand.getIdentifier(), frequencyBand); + } + break; + case OSP_NETWORK_STATUS_BROADCAST: + if(tsbk instanceof NetworkStatusBroadcast) + { + mTSBKNetworkStatusBroadcast = (NetworkStatusBroadcast)tsbk; + } + break; + case OSP_SYSTEM_SERVICE_BROADCAST: + if(tsbk instanceof SystemServiceBroadcast) + { + mTSBKSystemServiceBroadcast = (SystemServiceBroadcast)tsbk; + } + break; + case OSP_RFSS_STATUS_BROADCAST: + if(tsbk instanceof RFSSStatusBroadcast) + { + mTSBKRFSSStatusBroadcast = (RFSSStatusBroadcast)tsbk; + } + break; + case OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST: + if(tsbk instanceof SecondaryControlChannelBroadcast) + { + SecondaryControlChannelBroadcast sccb = (SecondaryControlChannelBroadcast)tsbk; + + for(IChannelDescriptor secondaryControlChannel : sccb.getChannels()) + { + mSecondaryControlChannels.put(secondaryControlChannel.toString(), secondaryControlChannel); + } + } + break; + case OSP_ADJACENT_STATUS_BROADCAST: + if(tsbk instanceof AdjacentStatusBroadcast) + { + AdjacentStatusBroadcast asb = (AdjacentStatusBroadcast)tsbk; + mTSBKNeighborSites.put((int)asb.getSite().getValue(), asb); + } + break; + case OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + if(tsbk instanceof SNDCPDataChannelAnnouncementExplicit) + { + mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)tsbk; + } + break; + } + } + + /** + * Processes Alternate Multi-Block Trunking Control (AMBTC) messages for network configuration details + */ + public void process(AMBTCMessage ambtc) + { + switch(ambtc.getHeader().getOpcode()) + { + case OSP_ADJACENT_STATUS_BROADCAST: + if(ambtc instanceof AMBTCAdjacentStatusBroadcast) + { + AMBTCAdjacentStatusBroadcast aasb = (AMBTCAdjacentStatusBroadcast)ambtc; + mAMBTCNeighborSites.put((int)aasb.getSite().getValue(), aasb); + } + break; +//TODO: process the rest of the messages here + } + } + + /** + * Processes Link Control Word (LCW) messages with network configuration details + */ + public void process(LinkControlWord lcw) + { + if(lcw.isValid()) + { + switch(lcw.getOpcode()) + { + case ADJACENT_SITE_STATUS_BROADCAST: + if(lcw instanceof LCAdjacentSiteStatusBroadcast) + { + LCAdjacentSiteStatusBroadcast assb = (LCAdjacentSiteStatusBroadcast)lcw; + mLCNeighborSites.put((int)assb.getSite().getValue(), assb); + } + break; + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + if(lcw instanceof LCAdjacentSiteStatusBroadcastExplicit) + { + LCAdjacentSiteStatusBroadcastExplicit assbe = (LCAdjacentSiteStatusBroadcastExplicit)lcw; + mLCNeighborSitesExplicit.put((int)assbe.getSite().getValue(), assbe); + } + break; + case CHANNEL_IDENTIFIER_UPDATE: + case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: + if(lcw instanceof IFrequencyBand) + { + IFrequencyBand band = (IFrequencyBand)lcw; + mFrequencyBandMap.put(band.getIdentifier(), band); + } + break; + case NETWORK_STATUS_BROADCAST: + if(lcw instanceof LCNetworkStatusBroadcast) + { + mLCNetworkStatusBroadcast = (LCNetworkStatusBroadcast)lcw; + } + break; + case NETWORK_STATUS_BROADCAST_EXPLICIT: + if(lcw instanceof LCNetworkStatusBroadcastExplicit) + { + mLCNetworkStatusBroadcastExplicit = (LCNetworkStatusBroadcastExplicit)lcw; + } + break; + case RFSS_STATUS_BROADCAST: + if(lcw instanceof LCRFSSStatusBroadcast) + { + mLCRFSSStatusBroadcast = (LCRFSSStatusBroadcast)lcw; + } + break; + case RFSS_STATUS_BROADCAST_EXPLICIT: + if(lcw instanceof LCRFSSStatusBroadcastExplicit) + { + mLCRFSSStatusBroadcastExplicit = (LCRFSSStatusBroadcastExplicit)lcw; + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + if(lcw instanceof LCSecondaryControlChannelBroadcast) + { + LCSecondaryControlChannelBroadcast sccb = (LCSecondaryControlChannelBroadcast)lcw; + + for(IChannelDescriptor channel : sccb.getChannels()) + { + mSecondaryControlChannels.put(channel.toString(), channel); + } + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + if(lcw instanceof LCSecondaryControlChannelBroadcastExplicit) + { + LCSecondaryControlChannelBroadcastExplicit sccb = (LCSecondaryControlChannelBroadcastExplicit)lcw; + for(IChannelDescriptor channel : sccb.getChannels()) + { + mSecondaryControlChannels.put(channel.toString(), channel); + } + } + break; + case SYSTEM_SERVICE_BROADCAST: + if(lcw instanceof LCSystemServiceBroadcast) + { + mLCSystemServiceBroadcast = (LCSystemServiceBroadcast)lcw; + } + break; + } + + } + + } + + public void reset() + { + + } + + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append("Activity Summary - Decoder:P25 ").append(mModulation.getLabel()); + + sb.append("\n\nNetwork\n"); + if(mTSBKNetworkStatusBroadcast != null) + { + sb.append(" NAC:").append(mTSBKNetworkStatusBroadcast.getNAC()); + sb.append(" WACN:").append(mTSBKNetworkStatusBroadcast.getWacn()); + sb.append(" SYSTEM:").append(mTSBKNetworkStatusBroadcast.getSystem()); + sb.append(" LRA:").append(mTSBKNetworkStatusBroadcast.getLocationRegistrationArea()); + } + else if(mLCNetworkStatusBroadcast != null) + { + sb.append(" WACN:").append(mLCNetworkStatusBroadcast.getWACN()); + sb.append(" SYSTEM:").append(mLCNetworkStatusBroadcast.getSystem()); + } + else if(mLCNetworkStatusBroadcastExplicit != null) + { + sb.append(" WACN:").append(mLCNetworkStatusBroadcastExplicit.getWACN()); + sb.append(" SYSTEM:").append(mLCNetworkStatusBroadcastExplicit.getSystem()); + } + else + { + sb.append(" UNKNOWN"); + } + + sb.append("\n\nCurrent Site\n"); + if(mTSBKRFSSStatusBroadcast != null) + { + sb.append(" SYSTEM:").append(mTSBKRFSSStatusBroadcast.getSystem()); + sb.append(" SITE:").append(mTSBKRFSSStatusBroadcast.getSite()); + sb.append(" RF SUBSYSTEM:").append(mTSBKRFSSStatusBroadcast.getRfss()); + sb.append(" LOCATION REGISTRATION AREA:").append(mTSBKRFSSStatusBroadcast.getLocationRegistrationArea()); + sb.append(" STATUS:").append(mTSBKRFSSStatusBroadcast.isActiveNetworkConnectionToRfssControllerSite() ? + "ACTIVE RFSS NETWORK CONNECTION\n" : "\n"); + sb.append(" PRI CONTROL CHANNEL:").append(mTSBKRFSSStatusBroadcast.getChannel()); + sb.append(" DOWNLINK:").append(mTSBKRFSSStatusBroadcast.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(mTSBKRFSSStatusBroadcast.getChannel().getUplinkFrequency()).append("\n"); + } + else if(mLCRFSSStatusBroadcast != null) + { + sb.append(" SYSTEM:").append(mLCRFSSStatusBroadcast.getSystem()); + sb.append(" SITE:").append(mLCRFSSStatusBroadcast.getSite()); + sb.append(" RF SUBSYSTEM:").append(mLCRFSSStatusBroadcast.getRfss()); + sb.append(" LOCATION REGISTRATION AREA:").append(mLCRFSSStatusBroadcast.getLocationRegistrationArea()).append("\n"); + sb.append(" PRI CONTROL CHANNEL:").append(mLCRFSSStatusBroadcast.getChannel()); + sb.append(" DOWNLINK:").append(mLCRFSSStatusBroadcast.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(mLCRFSSStatusBroadcast.getChannel().getUplinkFrequency()).append("\n"); + } + else if(mLCRFSSStatusBroadcastExplicit != null) + { + sb.append(" SITE:").append(mLCRFSSStatusBroadcastExplicit.getSite()); + sb.append(" RF SUBSYSTEM:").append(mLCRFSSStatusBroadcastExplicit.getRfss()); + sb.append(" LOCATION REGISTRATION AREA:").append(mLCRFSSStatusBroadcastExplicit.getLocationRegistrationArea()).append("\n"); + sb.append(" PRI CONTROL CHANNEL:").append(mLCRFSSStatusBroadcastExplicit.getChannel()); + sb.append(" DOWNLINK:").append(mLCRFSSStatusBroadcastExplicit.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(mLCRFSSStatusBroadcastExplicit.getChannel().getUplinkFrequency()).append("\n"); + } + else + { + sb.append(" UNKNOWN"); + } + + if(!mSecondaryControlChannels.isEmpty()) + { + List channels = new ArrayList<>(mSecondaryControlChannels.keySet()); + Collections.sort(channels); + + for(String channel : channels) + { + IChannelDescriptor secondaryControlChannel = mSecondaryControlChannels.get(channel); + + if(secondaryControlChannel != null) + { + sb.append(" SEC CONTROL CHANNEL:").append(secondaryControlChannel); + sb.append(" DOWNLINK:").append(secondaryControlChannel.getDownlinkFrequency()); + sb.append(" UPLINK:").append(secondaryControlChannel.getUplinkFrequency()).append("\n"); + } + } + } + + if(mSNDCPDataChannel != null) + { + sb.append(" CURRENT DATA CHANNEL:").append(mSNDCPDataChannel.getChannel()); + sb.append(" DOWNLINK:").append(mSNDCPDataChannel.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(mSNDCPDataChannel.getChannel().getUplinkFrequency()).append("\n"); + } + + if(mTSBKSystemServiceBroadcast != null) + { + sb.append(" AVAILABLE SERVICES:").append(mTSBKSystemServiceBroadcast.getAvailableServices()); + sb.append(" SUPPORTED SERVICES:").append(mTSBKSystemServiceBroadcast.getSupportedServices()); + } + else if(mLCSystemServiceBroadcast != null) + { + sb.append(" AVAILABLE SERVICES:").append(mTSBKSystemServiceBroadcast.getAvailableServices()); + sb.append(" SUPPORTED SERVICES:").append(mTSBKSystemServiceBroadcast.getSupportedServices()); + } + + + sb.append("\nNeighbor Sites\n"); + Set sites = new TreeSet<>(); + sites.addAll(mAMBTCNeighborSites.keySet()); + sites.addAll(mLCNeighborSites.keySet()); + sites.addAll(mLCNeighborSitesExplicit.keySet()); + sites.addAll(mTSBKNeighborSites.keySet()); + + if(sites.isEmpty()) + { + sb.append(" UNKNOWN"); + } + else + { + List sitesSorted = new ArrayList<>(sites); + Collections.sort(sitesSorted); + + for(Integer site : sitesSorted) + { + if(mAMBTCNeighborSites.containsKey(site)) + { + AMBTCAdjacentStatusBroadcast ambtc = mAMBTCNeighborSites.get(site); + sb.append(" SYSTEM:").append(ambtc.getSystem()); + sb.append(" SITE:").append(ambtc.getSite()); + sb.append(" LRA:").append(ambtc.getLocationRegistrationArea()); + sb.append(" RFSS:").append(ambtc.getRfss()); + sb.append(" CHANNEL:").append(ambtc.getChannel()); + sb.append(" DOWNLINK:").append(ambtc.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(ambtc.getChannel().getUplinkFrequency()).append("\n"); + } + if(mLCNeighborSites.containsKey(site)) + { + LCAdjacentSiteStatusBroadcast lc = mLCNeighborSites.get(site); + sb.append(" SYSTEM:").append(lc.getSystem()); + sb.append(" SITE:").append(lc.getSite()); + sb.append(" LRA:").append(lc.getLocationRegistrationArea()); + sb.append(" RFSS:").append(lc.getRfss()); + sb.append(" CHANNEL:").append(lc.getChannel()); + sb.append(" DOWNLINK:").append(lc.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(lc.getChannel().getUplinkFrequency()).append("\n"); + + } + if(mLCNeighborSitesExplicit.containsKey(site)) + { + LCAdjacentSiteStatusBroadcastExplicit lce = mLCNeighborSitesExplicit.get(site); + sb.append(" SYSTEM:---"); + sb.append(" SITE:").append(lce.getSite()); + sb.append(" LRA:").append(lce.getLocationRegistrationArea()); + sb.append(" RFSS:").append(lce.getRfss()); + sb.append(" CHANNEL:").append(lce.getChannel()); + sb.append(" DOWNLINK:").append(lce.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(lce.getChannel().getUplinkFrequency()).append("\n"); + } + if(mTSBKNeighborSites.containsKey(site)) + { + AdjacentStatusBroadcast asb = mTSBKNeighborSites.get(site); + sb.append(" SYSTEM:").append(asb.getSystem()); + sb.append(" SITE:").append(asb.getSite()); + sb.append(" LRA:").append(asb.getLocationRegistrationArea()); + sb.append(" RFSS:").append(asb.getRfss()); + sb.append(" CHANNEL:").append(asb.getChannel()); + sb.append(" DOWNLINK:").append(asb.getChannel().getDownlinkFrequency()); + sb.append(" UPLINK:").append(asb.getChannel().getUplinkFrequency()); + sb.append(" STATUS:").append(asb.getSiteFlags()).append("\n"); + } + } + } + + sb.append("\nFrequency Bands\n"); + if(mFrequencyBandMap.isEmpty()) + { + sb.append(" UNKNOWN"); + } + else + { + List ids = new ArrayList<>(mFrequencyBandMap.keySet()); + Collections.sort(ids); + { + for(Integer id : ids) + { + sb.append(" ").append(formatFrequencyBand(mFrequencyBandMap.get(id))).append("\n"); + } + } + } + + return sb.toString(); + } + + /** + * Formats a frequency band + */ + private String formatFrequencyBand(IFrequencyBand band) + { + StringBuilder sb = new StringBuilder(); + sb.append("BAND:").append(band.getIdentifier()); + sb.append(" ").append(band.isTDMA() ? "TDMA" : "FDMA"); + sb.append(" BASE:").append(band.getBaseFrequency()); + sb.append(" BANDWIDTH:").append(band.getBandwidth()); + sb.append(" SPACING:").append(band.getChannelSpacing()); + sb.append(" TRANSMIT OFFSET:").append(band.getTransmitOffset()); + + if(band.isTDMA()) + { + sb.append(" TIMESLOTS:").append(band.getTimeslotCount()); + } + + return sb.toString(); + } + +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/AccessType.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/AccessType.java new file mode 100644 index 00000000..7f26645a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/AccessType.java @@ -0,0 +1,31 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Access types + */ +public enum AccessType +{ + FDMA, + TDMA, + UNKNOWN; +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/AnswerResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/AnswerResponse.java new file mode 100644 index 00000000..724cec6b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/AnswerResponse.java @@ -0,0 +1,24 @@ +package io.github.dsheirer.module.decode.p25.reference; + +public enum AnswerResponse +{ + PROCEED, + DENY, + WAIT, + UNKNOWN; + + public static AnswerResponse fromValue(int value) + { + switch(value) + { + case 0x20: + return PROCEED; + case 0x21: + return DENY; + case 0x22: + return WAIT; + default: + return UNKNOWN; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/CancelReason.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/CancelReason.java new file mode 100644 index 00000000..0966299e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/CancelReason.java @@ -0,0 +1,46 @@ +package io.github.dsheirer.module.decode.p25.reference; + +public enum CancelReason +{ + NO_REASON("NO REASON"), + TERMINATE_QUEUED_CONDITION("TERMINATE QUEUED CONDITION"), + TERMINATE_RESOURCE_ASSIGNMENT("TERMINATE RESOURCE ASSIGNMENT"), + RESERVED("RESERVED"), + USER_OR_SYSTEM_DEFINED("USER OR SYSTEM DEFINED"), + UNKNOWN("UNKNOWN"); + + private String mLabel; + + CancelReason(String label) + { + mLabel = label; + } + + @Override + public String toString() + { + return mLabel; + } + + public static CancelReason fromCode(int code) + { + if(code == 0x00) + { + return CancelReason.NO_REASON; + } + else if(code == 0x10) + { + return TERMINATE_QUEUED_CONDITION; + } + else if(code == 0x20) + { + return TERMINATE_RESOURCE_ASSIGNMENT; + } + else if(code < 0x7F) + { + return RESERVED; + } + + return USER_OR_SYSTEM_DEFINED; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Capability.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Capability.java new file mode 100644 index 00000000..18efe066 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Capability.java @@ -0,0 +1,123 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Capability bitmap parser for FDMA/TDMA capabilities + */ +public class Capability +{ + private static int VALID_FLAG = 0x40; + private static int TDMA_HALF_RATE_2_SLOT_FLAG = 0x20; + private static int TDMA_HALF_RATE_4_SLOT_FLAG = 0x10; + private static int FDMA_HALF_RATE_FLAG = 0x08; + private static int TDMA_8PSK_FLAG = 0x04; + + private int mCapability; + + /** + * Constructs a capability bitmap parser + * @param capability + */ + public Capability(int capability) + { + mCapability = capability; + } + + /** + * Indicates that the bitmap contains valid values. This is an all or nothing field. + */ + public boolean isValid() + { + return (mCapability & VALID_FLAG) == VALID_FLAG; + } + + /** + * Indicates the device supports 2-slot TDMA and the Half-Rate vocoder + */ + public boolean isTDMAHalfRate2Slot() + { + return isValid() && (mCapability & TDMA_HALF_RATE_2_SLOT_FLAG) == TDMA_HALF_RATE_2_SLOT_FLAG; + } + + /** + * Indicates the device supports 4-slot TDMA and the Half-Rate vocoder + */ + public boolean isTDMAHalfRate4Slot() + { + return isValid() && (mCapability & TDMA_HALF_RATE_4_SLOT_FLAG) == TDMA_HALF_RATE_4_SLOT_FLAG; + } + + /** + * Indicates the device supports the FDMA Half-Rate vocoder (in addition to the standard Full-Rate) + */ + public boolean isFDMAHalfRate() + { + return isValid() && (mCapability & FDMA_HALF_RATE_FLAG) == FDMA_HALF_RATE_FLAG; + } + + /** + * Indicates the device supports TDMA with 8PSK modulation + */ + public boolean isTDMA8PSK() + { + return isValid() && (mCapability & TDMA_8PSK_FLAG) == TDMA_8PSK_FLAG; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("FDMA/FULL-RATE"); + + if(isFDMAHalfRate()) + { + sb.append("/HALF-RATE"); + } + + if(isTDMAHalfRate2Slot() || isTDMAHalfRate4Slot() || isTDMA8PSK()) + { + sb.append(" TDMA"); + + if(isTDMAHalfRate2Slot() || isTDMAHalfRate4Slot()) + { + sb.append("/HALF-RATE"); + } + + if(isTDMAHalfRate2Slot()) + { + sb.append("/2-SLOT"); + } + + if(isTDMAHalfRate4Slot()) + { + sb.append("/4-SLOT"); + } + + if(isTDMA8PSK()) + { + sb.append("/8PSK"); + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ChannelType.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ChannelType.java new file mode 100644 index 00000000..20f1cb37 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ChannelType.java @@ -0,0 +1,105 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +public enum ChannelType +{ + TYPE_0(AccessType.FDMA, 12500, 1, Vocoder.HALF_RATE), + TYPE_1(AccessType.FDMA, 12500, 1, Vocoder.FULL_RATE), + TYPE_2(AccessType.FDMA, 6250, 1, Vocoder.HALF_RATE), + TYPE_3(AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE), + TYPE_4(AccessType.TDMA, 25000, 4, Vocoder.HALF_RATE), + TYPE_5(AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE), //HD8PSK simulcast + UNKNOWN(AccessType.UNKNOWN, 0, 0, Vocoder.HALF_RATE); + + private AccessType mAccessType; + private int mBandwidth; + private int mSlotsPerCarrier; + private Vocoder mVocoder; + + ChannelType(AccessType accessType, int bandwidth, int slots, Vocoder vocoder) + { + mAccessType = accessType; + mBandwidth = bandwidth; + mSlotsPerCarrier = slots; + mVocoder = vocoder; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(isTDMA()) + { + sb.append("TDMA"); + if(this.equals(TYPE_5)) + { + sb.append(" H-D8PSK"); + } + sb.append(" BW:" + mBandwidth); + sb.append(" TIMESLOTS:" + mSlotsPerCarrier); + sb.append(" VOCODER:" + mVocoder.name()); + } + else + { + sb.append("FDMA"); + sb.append(" BW:" + mBandwidth); + sb.append(" VOCODER:" + mVocoder.name()); + } + + return sb.toString(); + } + + public boolean isTDMA() + { + return getAccessType() == AccessType.TDMA; + } + + public static ChannelType fromValue(int value) + { + if(0 <= value && value <= 5) + { + return ChannelType.values()[value]; + } + + return ChannelType.UNKNOWN; + } + + public AccessType getAccessType() + { + return mAccessType; + } + + public int getBandwidth() + { + return mBandwidth; + } + + public int getSlotsPerCarrier() + { + return mSlotsPerCarrier; + } + + public Vocoder getVocoder() + { + return mVocoder; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataServiceOptions.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataServiceOptions.java new file mode 100644 index 00000000..06d7548e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataServiceOptions.java @@ -0,0 +1,59 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.decode.p25.reference; + +public class DataServiceOptions extends ServiceOptions +{ + private static final int NSAPI = 0x0F; + + public DataServiceOptions(int serviceOptions) + { + super(serviceOptions); + } + + /** + * Network Service Access Point ID + */ + public int getNSAPI() + { + return (mServiceOptions & NSAPI); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NSAPI:").append(getNSAPI()); + + if(isEncrypted()) + { + sb.append(" ENCRYPTED"); + } + + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + + sb.append(" ").append(getSessionMode()); + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataSubscriberUnitType.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataSubscriberUnitType.java new file mode 100644 index 00000000..8b30a438 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataSubscriberUnitType.java @@ -0,0 +1,56 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Data Subscriber (ie mobile radio/terminal) Unit Type + */ +public enum DataSubscriberUnitType +{ + DATA_ONLY("DATA ONLY"), + DATA_OR_VOICE("DATA/VOICE"), + RESERVED("RESERVED"); + + private String mLabel; + + DataSubscriberUnitType(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static DataSubscriberUnitType fromValue(int value) + { + switch(value) + { + case 0: + return DATA_ONLY; + case 1: + return DATA_OR_VOICE; + default: + return RESERVED; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataUnitID.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataUnitID.java index a0498efb..ef63a6e3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataUnitID.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DataUnitID.java @@ -1,94 +1,139 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ package io.github.dsheirer.module.decode.p25.reference; /** - * The Data Unit ID (DUID) is part of the Network ID (NID) field and indicates - * the type of message. + * The Data Unit ID (DUID) is part of the Network ID (NID) field and indicates the type of message. */ public enum DataUnitID { - NID ( -1, 64, false, "NID ", "Network and Data Unit ID" ), - HDU ( 0, 712, false, "HDU ", "Header Data Unit" ), - TDU ( 3, 64, false, "TDU ", "Simple Terminator Data Unit" ), - LDU1 ( 5, 1632, true, "LDU1 ", "Logical Link Data Unit 1" ), - VSELP1( 6, 1680, false, "VSEL1", "Motorola VSELP Unit 1" ), - TSBK1( 7, 260, false, "TSBK1", "Trunking Signaling Block" ), - TSBK2( 7, 260, false, "TSBK2", "Trunking Signaling Block" ), - TSBK3( 7, 260, false, "TSBK3", "Trunking Signaling Block" ), - VSELP2( 9, 1680, false, "VSEL2", "Motorola VSELP Unit 2" ), - LDU2 ( 10, 1632, true, "LDU2 ", "Logical Link Data Unit 2" ), - PDU0 ( 12, 260, false, "PDU0 ", "Packet Header Data Unit" ), - PDU1 ( 12, 356, false, "PDU1 ", "Packet Data Unit-Unconfirmed" ), - PDU2 ( 12, 452, false, "PDU2 ", "Packet Data Unit-Unconfirmed" ), - PDU3 ( 12, 548, false, "PDU3 ", "Packet Data Unit-Unconfirmed" ), - PDUC ( 12, 356, false, "PDUC", "Packet Data Unit-Confirmed" ), - TDULC( 15, 352, false, "TDULC", "Terminator Data Unit With Link Control" ), - UNKN ( -1, 0, false, "UNKWN", "Unknown" ); - - private int mValue; - private int mMessageLength; - private boolean mParity; - private String mLabel; - private String mDescription; - - private DataUnitID( int value, int length, boolean parity, String label, - String description ) - { - mValue = value; - mMessageLength = length; - mParity = parity; - mLabel = label; - mDescription = description; - } - - public int getValue() - { - return mValue; - } - - public int getMessageLength() - { - return mMessageLength; - } - - public String getLabel() - { - return mLabel; - } - - public String getDescription() - { - return mDescription; - } - - public boolean getParity() - { - return mParity; - } - - public static DataUnitID fromValue( int value ) - { - switch( value ) - { - case 0: - return HDU; - case 3: - return TDU; - case 5: - return LDU1; - case 6: - return VSELP1; - case 7: - return TSBK1; - case 9: - return VSELP2; - case 10: - return LDU2; - case 12: - return PDU0; - case 15: - return TDULC; - default: - return UNKN; - } - } + HEADER_DATA_UNIT(0, 648 + 10, true, "HDU "), + UNKNOWN_1(1, -1, false, "UNK01"), + UNKNOWN_2(2, -1, false, "UNK02"), + TERMINATOR_DATA_UNIT(3, 28, true, "TDU "), + UNKNOWN_4(4, -1, false, "UNK04"), + LOGICAL_LINK_DATA_UNIT_1(5, 1568, true, "LDU1 "), + VSELP1(6, 64 + 1616, false, "VSEL1"), + UNKNOWN_8(8, -1, false, "UNK08"), + VSELP2(9, 1616 + 64, false, "VSEL2"), + LOGICAL_LINK_DATA_UNIT_2(10, 1568, true, "LDU2 "), + UNKNOWN_11(11, -1, false, "UNK11"), + PACKET_HEADER_DATA_UNIT(12, 196, false, "PDUH "), + PACKET_DATA_UNIT(-1, 196, false, "PDU "), + UNKNOWN_13(13, -1, false, "UNK13"), + UNKNOWN_14(14, -1, false, "UNK14"), + TERMINATOR_DATA_UNIT_LINK_CONTROL(15, 308, true, "TDULC"), + + //The following are not true data unit identifiers, rather they are used as identifiers + ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL(-1, -1, false, "AMBTC"), + IP_PACKET_DATA(-1, 0, false, "IPPKT"), + SUBNETWORK_DEPENDENT_CONVERGENCE_PROTOCOL(-1, 0, false, "SNDCP"), + TRUNKING_SIGNALING_BLOCK_1(7, 196, false, "TSBK1"), + TRUNKING_SIGNALING_BLOCK_2(7, 196, false, "TSBK2"), + TRUNKING_SIGNALING_BLOCK_3(7, 196, false, "TSBK3"), + UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL(-1, -1, false, "UMBTC"), + + UNKNOWN(-1, -1, false, "UNKN "); + + private int mValue; + private int mMessageLength; + private boolean mTrailingStatusDibit; + private String mLabel; + + DataUnitID(int value, int length, boolean trailingStatusDibit, String label) + { + mValue = value; + mMessageLength = length; + mTrailingStatusDibit = trailingStatusDibit; + mLabel = label; + } + + /** + * Data Unit ID value + */ + public int getValue() + { + return mValue; + } + + /** + * Length of the message in bits + */ + public int getMessageLength() + { + return mMessageLength; + } + + /** + * Short display label + */ + public String getLabel() + { + return mLabel; + } + + /** + * Indicates if the message has a trailing status dibit that must be processed + */ + public boolean hasTrailingStatusDibit() + { + return mTrailingStatusDibit; + } + + /** + * Lookup the Data Unit ID from an integer value + */ + public static DataUnitID fromValue(int value) + { + switch(value) + { + case 0: + return HEADER_DATA_UNIT; + case 1: + return UNKNOWN_1; + case 2: + return UNKNOWN_2; + case 3: + return TERMINATOR_DATA_UNIT; + case 4: + return UNKNOWN_4; + case 5: + return LOGICAL_LINK_DATA_UNIT_1; + case 6: + return VSELP1; + case 7: + return TRUNKING_SIGNALING_BLOCK_1; + case 8: + return UNKNOWN_8; + case 9: + return VSELP2; + case 10: + return LOGICAL_LINK_DATA_UNIT_2; + case 11: + return UNKNOWN_11; + case 12: + return PACKET_HEADER_DATA_UNIT; + case 13: + return UNKNOWN_13; + case 14: + return UNKNOWN_14; + case 15: + return TERMINATOR_DATA_UNIT_LINK_CONTROL; + default: + throw new IllegalArgumentException("Data Unit ID must be in range 0 - 15"); + } + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Digit.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Digit.java index 7e2e2231..0c4b14a3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Digit.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Digit.java @@ -20,7 +20,7 @@ public enum Digit D13( "HOOK FLASH", "-" ), D14( "PAUSE", "-" ), D15( "ESC", "NULL" ), - DUNK( "?", "?" ); + UNKNOWN( "?", "?" ); private String mValue; private String mEscapedValue; @@ -48,7 +48,7 @@ public enum Digit return values()[ value ]; } - return DUNK; + return UNKNOWN; } public static String decode( List values ) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Direction.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Direction.java new file mode 100644 index 00000000..0f93a80e --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Direction.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Message direction + */ +public enum Direction +{ + /** + * Inbound: MOBILE >> REPEATER + */ + INBOUND("ISP"), + + /** + * Output: REPEATER >> MOBILE + */ + OUTBOUND("OSP"); + + private String mLabel; + + Direction(String label) + { + mLabel = label; + } + + /** + * Lookup method for direction. + * + * @param direction + * @return + */ + public static Direction fromValue(boolean direction) + { + return (direction ? OUTBOUND : INBOUND); + } + + /** + * Overrides the .name() method to provide a label + */ + @Override + public String toString() + { + return mLabel; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Duplex.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Duplex.java new file mode 100644 index 00000000..f530662b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Duplex.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.reference; + +public enum Duplex +{ + HALF, FULL +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ExtendedFunction.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ExtendedFunction.java index 2a868ea6..77ef2f2c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ExtendedFunction.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ExtendedFunction.java @@ -2,70 +2,76 @@ package io.github.dsheirer.module.decode.p25.reference; public enum ExtendedFunction { - RADIO_CHECK( 0x0000, "RADIO CHECK" ), - RADIO_DETACH( 0x007D, "RADIO DETACH" ), - RADIO_UNINHIBIT( 0x007E, "RADIO UNINHIBIT" ), - RADIO_INHIBIT( 0x007F, "RADIO INHIBIT" ), - RADIO_CHECK_ACK( 0x0080, "RADIO CHECK ACK" ), - RADIO_DETACH_ACK( 0x00FD, "RADIO DETACH ACK" ), - RADIO_UNINHIBIT_ACK( 0x00FE, "RADIO UNINHIBIT ACK" ), - RADIO_INHIBIT_ACK( 0x00FF, "RADIO INHIBIT ACK" ), - - GROUP_CONTROL_COMMAND( 0x0100, "GROUP CONTROL COMMAND" ), - UNIT_DYNAMIC_COMMAND( 0x0200, "UNIT DYNAMIC COMMAND" ), - GROUP_DYNAMIC_COMMAND( 0x0300, "GROUP DYNAMIC COMMAND" ), - - UNKNOWN( -1, "UNKNOWN" ); + RADIO_CHECK(0x0000, "RADIO CHECK"), + RADIO_DETACH(0x007D, "RADIO DETACH"), + RADIO_UNINHIBIT(0x007E, "RADIO UNINHIBIT"), + RADIO_INHIBIT(0x007F, "RADIO INHIBIT"), + RADIO_CHECK_ACK(0x0080, "RADIO CHECK ACK"), + RADIO_DETACH_ACK(0x00FD, "RADIO DETACH ACK"), + RADIO_UNINHIBIT_ACK(0x00FE, "RADIO UNINHIBIT ACK"), + RADIO_INHIBIT_ACK(0x00FF, "RADIO INHIBIT ACK"), - private int mFunction; - private String mLabel; - - private ExtendedFunction( int function, String label ) - { - mFunction = function; - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static ExtendedFunction fromValue( int function ) - { - switch( function ) - { - case 0x0000: - return RADIO_CHECK; - case 0x007D: - return RADIO_DETACH; - case 0x007E: - return RADIO_UNINHIBIT; - case 0x007F: - return RADIO_INHIBIT; - case 0x0080: - return RADIO_CHECK_ACK; - case 0x00FD: - return RADIO_DETACH_ACK; - case 0x00FE: - return RADIO_UNINHIBIT_ACK; - case 0x00FF: - return RADIO_INHIBIT_ACK; - } - - if( ( function & 0x0100 ) == 0x0100 ) - { - return GROUP_CONTROL_COMMAND; - } - if( ( function & 0x0200 ) == 0x0200 ) - { - return UNIT_DYNAMIC_COMMAND; - } - if( ( function & 0x0300 ) == 0x0300 ) - { - return GROUP_DYNAMIC_COMMAND; - } + GROUP_CONTROL_COMMAND(0x0100, "GROUP CONTROL COMMAND"), + UNIT_DYNAMIC_COMMAND(0x0200, "UNIT DYNAMIC COMMAND"), + GROUP_DYNAMIC_COMMAND(0x0300, "GROUP DYNAMIC COMMAND"), - return UNKNOWN; - } + UNKNOWN(-1, "UNKNOWN"); + + private int mFunction; + private String mLabel; + + ExtendedFunction(int function, String label) + { + mFunction = function; + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + @Override + public String toString() + { + return mLabel; + } + + public static ExtendedFunction fromValue(int function) + { + switch(function) + { + case 0x0000: + return RADIO_CHECK; + case 0x007D: + return RADIO_DETACH; + case 0x007E: + return RADIO_UNINHIBIT; + case 0x007F: + return RADIO_INHIBIT; + case 0x0080: + return RADIO_CHECK_ACK; + case 0x00FD: + return RADIO_DETACH_ACK; + case 0x00FE: + return RADIO_UNINHIBIT_ACK; + case 0x00FF: + return RADIO_INHIBIT_ACK; + } + + if((function & 0x0100) == 0x0100) + { + return GROUP_CONTROL_COMMAND; + } + if((function & 0x0200) == 0x0200) + { + return UNIT_DYNAMIC_COMMAND; + } + if((function & 0x0300) == 0x0300) + { + return GROUP_DYNAMIC_COMMAND; + } + + return UNKNOWN; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/IPHeaderCompression.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/IPHeaderCompression.java index 67dbe7e2..6de90d39 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/IPHeaderCompression.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/IPHeaderCompression.java @@ -1,3 +1,23 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.p25.reference; public enum IPHeaderCompression @@ -10,7 +30,7 @@ public enum IPHeaderCompression private int mValue; private String mLabel; - private IPHeaderCompression( int value, String label ) + IPHeaderCompression( int value, String label ) { mValue = value; mLabel = label; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/LinkControlOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/LinkControlOpcode.java deleted file mode 100644 index 18e14ecd..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/LinkControlOpcode.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.github.dsheirer.module.decode.p25.reference; - -public enum LinkControlOpcode -{ - GROUP_VOICE_CHANNEL_USER( "GRP_VCH_USER ", "Group Voice Channel User", 0 ), - RESERVED_01( "RESERVED_01 ", "Reserved", 1 ), - GROUP_VOICE_CHANNEL_UPDATE( "GRP_VCH_UPDATE ", "Group Voice Channel Grant Update", 2 ), - UNIT_TO_UNIT_VOICE_CHANNEL_USER( "UU_VCH_USER ", "Unit-to-Unit Voice Channel User", 3 ), - GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT( "GRP_VCH_UPD_EXPL", "Group Voice Channel Update Explicit", 4 ), - UNIT_TO_UNIT_ANSWER_REQUEST( "UU_ANS_REQ ", "Unit-to-Unit Answer Request", 5 ), - TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER( "TEL_INT_VCH_USER", "Telephone Interconnect Voice Channel User", 6 ), - TELEPHONE_INTERCONNECT_ANSWER_REQUEST( "TEL_INT_ANS_RQST", "Telephone Interconnect Answer Request", 7 ), - RESERVED_08( "RESERVED_08 ", "Reserved", 8 ), - RESERVED_09( "RESERVED_09 ", "Reserved", 9 ), - RESERVED_0A( "RESERVED_0A ", "Reserved", 10 ), - RESERVED_0B( "RESERVED_0B ", "Reserved", 11 ), - RESERVED_0C( "RESERVED_0C ", "Reserved", 12 ), - RESERVED_0D( "RESERVED_0D ", "Reserved", 13 ), - RESERVED_0E( "RESERVED_0E ", "Reserved", 14 ), - CALL_TERMINATION_OR_CANCELLATION( "CALL TERMINATION", "Call Termination Cancellation", 15 ), - GROUP_AFFILIATION_QUERY( "GRP_AFFIL_QUERY ", "Group Affiliation Query", 16 ), - UNIT_REGISTRATION_COMMAND( "UNIT_REG_COMMAND", "Unit Registration Command", 17 ), - UNIT_AUTHENTICATION_COMMAND( "UNIT_AUTHEN_CMD ", "Unit Authentication Command", 18 ), - STATUS_QUERY( "STATUS QUERY ", "Status Query", 19 ), - STATUS_UPDATE( "STATUS_UPDATE ", "Status Update", 20 ), - MESSAGE_UPDATE( "MESSAGE UPDATE ", "Message Update", 21 ), - CALL_ALERT( "CALL ALERT", "Call Alert", 22 ), - EXTENDED_FUNCTION_COMMAND( "EXT FUNC COMMAND", "Extended Function Command", 23 ), - CHANNEL_IDENTIFIER_UPDATE( "CHAN IDEN UPDATE", "Channel Identifier Update", 24 ), - CHANNEL_IDENTIFIER_UPDATE_EXPLICIT( "CHAN IDEN UPD EX", "Channel Identifier Update Explicit", 25 ), - RESERVED_1A( "RESERVED_1A ", "Reserved", 26 ), - RESERVED_1B( "RESERVED_1B ", "Reserved", 27 ), - RESERVED_1C( "RESERVED_1C ", "Reserved", 28 ), - RESERVED_1D( "RESERVED_1D ", "Reserved", 29 ), - RESERVED_1E( "RESERVED_1E ", "Reserved", 30 ), - RESERVED_1F( "RESERVED_1F ", "Reserved", 31 ), - SYSTEM_SERVICE_BROADCAST( "SYS_SVC_BCAST ", "System Service Broadcast", 32 ), - SECONDARY_CONTROL_CHANNEL_BROADCAST( "SEC_CCH_BROADCST", "Secondary Control Channel Broadcast", 33 ), - ADJACENT_SITE_STATUS_BROADCAST( "ADJ SITE STATUS ", "Adjacent Site Status Broadcast", 34 ), - RFSS_STATUS_BROADCAST( "RFSS_STATUS_BCST", "RFSS Status Broadcast", 35 ), - NETWORK_STATUS_BROADCAST( "NET_STATUS_BCAST", "Network Status Broadcast", 36 ), - PROTECTION_PARAMETER_BROADCAST( "ENCRYPT_PAR_BCST", "Protection Parameter Broadcast", 37 ), - SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT( "SCCB_CCH_BCST_EX", "Secondary Control Channel Broadcast-Explicit", 38 ), - ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT( "ADJ SITE STAT EX", "Adjacent Site Status Broadcast Explicit", 39 ), - RFSS_STATUS_BROADCAST_EXPLICIT( "RFSS STAT BCST E", "RFSS Status Broadcast Explicit", 40 ), - NETWORK_STATUS_BROADCAST_EXPLICIT( "NET STAT BCAST E", "Network Status Broadcast", 41 ), - RESERVED_2A( "RESERVED_2A ", "Reserved", 42 ), - RESERVED_2B( "RESERVED_2B ", "Reserved", 43 ), - RESERVED_2C( "RESERVED_2C ", "Reserved", 44 ), - RESERVED_2D( "RESERVED_2D ", "Reserved", 45 ), - RESERVED_2E( "RESERVED_2E ", "Reserved", 46 ), - RESERVED_2F( "RESERVED_2F ", "Reserved", 47 ), - RESERVED_30( "RESERVED_30 ", "Reserved", 48 ), - RESERVED_31( "RESERVED_31 ", "Reserved", 49 ), - RESERVED_32( "RESERVED_32 ", "Reserved", 50 ), - RESERVED_33( "RESERVED_33 ", "Reserved", 51 ), - RESERVED_34( "RESERVED_34 ", "Reserved", 52 ), - RESERVED_35( "RESERVED_35 ", "Reserved", 53 ), - RESERVED_36( "RESERVED_36 ", "Reserved", 54 ), - RESERVED_37( "RESERVED_37 ", "Reserved", 55 ), - RESERVED_38( "RESERVED_38 ", "Reserved", 56 ), - RESERVED_39( "RESERVED_39 ", "Reserved", 57 ), - RESERVED_3A( "RESERVED_3A ", "Reserved", 58 ), - RESERVED_3B( "RESERVED_3B ", "Reserved", 59 ), - RESERVED_3C( "RESERVED_3C ", "Reserved", 60 ), - RESERVED_3D( "RESERVED_3D ", "Reserved", 61 ), - RESERVED_3E( "RESERVED_3E ", "Reserved", 62 ), - RESERVED_3F( "RESERVED_3F ", "Reserved", 63 ), - UNKNOWN( "UNKNOWN ", "Unknown", -1 ); - - private String mLabel; - private String mDescription; - private int mCode; - - private LinkControlOpcode( String label, String description, int code ) - { - mLabel = label; - mDescription = description; - mCode = code; - } - - public String getLabel() - { - return mLabel; - } - - public String getDescription() - { - return mDescription; - } - - public int getCode() - { - return mCode; - } - - public static LinkControlOpcode fromValue( int value ) - { - if( 0 <= value && value <= 63 ) - { - return values()[ value ]; - } - - return UNKNOWN; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/MaximumTransmitUnit.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/MaximumTransmitUnit.java new file mode 100644 index 00000000..9864e195 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/MaximumTransmitUnit.java @@ -0,0 +1,59 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +public enum MaximumTransmitUnit +{ + RESERVED(0), + BYTES_296(296), + BYTES_510(510), + BYTES_1020(1020), + BYTES_1500(1500); + + private int mByteCount; + + MaximumTransmitUnit(int byteCount) + { + mByteCount = byteCount; + } + + public int getByteCount() + { + return mByteCount; + } + + public static MaximumTransmitUnit fromValue(int value) + { + switch(value) + { + case 1: + return BYTES_296; + case 2: + return BYTES_510; + case 3: + return BYTES_1020; + case 4: + return BYTES_1500; + default: + return RESERVED; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/NetworkAddressType.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/NetworkAddressType.java new file mode 100644 index 00000000..c044b219 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/NetworkAddressType.java @@ -0,0 +1,74 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +/** + * SNDCP Network Address Type + */ +public enum NetworkAddressType +{ + IPV4_STATIC_ADDRESS("IPV4 STATIC ADDRESS"), + IPV4_DYNAMIC_ADDRESS("IPV4 DYNAMIC ADDRESS"), + RESERVED("RESERVED"), + NO_ADDRESS("NO ADDRESS"), + UNKNOWN("UNKNOWN"); + + private String mLabel; + + NetworkAddressType(String label) + { + mLabel = label; + } + + @Override + public String toString() + { + return mLabel; + } + + public static NetworkAddressType fromValue(int value) + { + switch(value) + { + case 0: + return IPV4_STATIC_ADDRESS; + case 1: + return IPV4_DYNAMIC_ADDRESS; + case 2: + case 3: + case 4: + case 5: + case 6: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + return RESERVED; + case 15: + return NO_ADDRESS; + default: + return UNKNOWN; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Opcode.java deleted file mode 100644 index 14b31c3b..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Opcode.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.github.dsheirer.module.decode.p25.reference; - -public enum Opcode -{ - GROUP_VOICE_CHANNEL_GRANT( "GRP_VCH_GRANT ", "Group Voice Channel Grant", 0 ), - RESERVED_01( "RESERVED_01 ", "Reserved", 1 ), - GROUP_VOICE_CHANNEL_GRANT_UPDATE( "GRP_VCH_GRNT_UPD", "Group Voice Channel Grant Update", 2 ), - GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT( "GRP_VCH_GRNT_UPX", "Group Voice Channel Grant Update - Explicit", 3 ), - UNIT_TO_UNIT_VOICE_CHANNEL_GRANT( "UU_VCH_GRANT ", "Unit-to-Unit Voice Channel Grant", 4 ), - UNIT_TO_UNIT_ANSWER_REQUEST( "UU_ANS_REQ ", "Unit-to-Unit Answer Request", 5 ), - UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE( "UU_VCH_GRANT_UPD", "Unit-to-Unit Voice Channel Grant Update", 6 ), - RESERVED_07( "RESERVED_07 ", "Reserved", 7 ), - TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT( "TEL_INT_VCH_GRNT", "Telephone Interconnect Voice Channel Grant", 8 ), - TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE( "TEL_INT_VCH_GRNU","Telephone Interconnect Voice Channel Grant Update", 9 ), - TELEPHONE_INTERCONNECT_ANSWER_REQUEST( "TEL_INT_ANS_RQST", "Telephone Interconnect Answer Request", 10 ), - RESERVED_0B( "RESERVED_0B ", "Reserved", 11 ), - RESERVED_0C( "RESERVED_0C ", "Reserved", 12 ), - RESERVED_0D( "RESERVED_0D ", "Reserved", 13 ), - RESERVED_0E( "RESERVED_0E ", "Reserved", 14 ), - RESERVED_0F( "RESERVED_0F ", "Reserved", 15 ), - INDIVIDUAL_DATA_CHANNEL_GRANT( "IND_DCH_GRANT ", "Individual Data Channel Grant", 16 ), - GROUP_DATA_CHANNEL_GRANT( "GRP_DCH_GRANT ", "Group Data Channel Grant", 17 ), - GROUP_DATA_CHANNEL_ANNOUNCEMENT( "GRP_DCH_ANNOUNCE", "Group Data Channel Announcement", 18 ), - GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT( "GRP_DCH_ANNC_EXP", "Group Data Channel Announcement-Explicit", 19 ), - SNDCP_DATA_CHANNEL_GRANT( "SNDCP_DCH_GRANT ", "SNDCP Data Channel Grant", 20 ), - SNDCP_DATA_PAGE_REQUEST( "SNDCP_DCH_PAG_RQ", "SNDCP Data Page Request", 21 ), - SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT( "SNDCP_DCH_ANN_EX", "SNDCP Data Channel Announcement Explicit", 22 ), - RESERVED_17( "RESERVED_17 ", "Reserved", 23 ), - STATUS_UPDATE( "STATUS_UPDATE ", "Status Update", 24 ), - RESERVED_19( "RESERVED_19 ", "Reserved", 25 ), - STATUS_QUERY( "STATUS_QUERY ", "Status Query", 26 ), - RESERVED_1B( "RESERVED_1B ", "Reserved", 27 ), - MESSAGE_UPDATE( "MESSAGE_UPDATE ", "Message Update", 28 ), - RADIO_UNIT_MONITOR_COMMAND( "RADIO_MONITR_CMD", "Radio Unit Monitor Command", 29 ), - RESERVED_1E( "RESERVED_1E ", "Reserved", 30 ), - CALL_ALERT( "CALL_ALERT ", "Call Alert", 31 ), - ACKNOWLEDGE_RESPONSE( "ACK_RESPONSE_FNE", "Acknowledge Response - FNE", 32 ), - QUEUED_RESPONSE( "QUEUED_RESPONSE ", "Queued Response", 33 ), - RESERVED_22( "RESERVED_22 ", "Reserved", 34 ), - RESERVED_23( "RESERVED_23 ", "Reserved", 35 ), - EXTENDED_FUNCTION_COMMAND( "EXTNDED_FUNC_CMD", "Extended Function Command", 36 ), - RESERVED_25( "RESERVED_25 ", "Reserved", 37 ), - RESERVED_26( "RESERVED_26 ", "Reserved", 38 ), - DENY_RESPONSE( "DENY_RESPONSE ", "Deny Response", 39 ), - GROUP_AFFILIATION_RESPONSE( "GRP_AFFIL_RESP ", "Group Affiliation Response", 40 ), - SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT( "SCCB_CCH_BCST_EX", "Secondary Control Channel Broadcast-Explicit", 41 ), - GROUP_AFFILIATION_QUERY( "GRP_AFFIL_QUERY ", "Group Affiliation Query", 42 ), - LOCATION_REGISTRATION_RESPONSE( "LOCN_REG_RESPONS", "Location Registration Response", 43 ), - UNIT_REGISTRATION_RESPONSE( "UNIT_REG_RESPONS", "Unit Registration Response", 44 ), - UNIT_REGISTRATION_COMMAND( "UNIT_REG_COMMAND", "Unit Registration Command", 45 ), - AUTHENTICATION_COMMAND( "AUTH_COMMAND ", "Authentication Command", 46 ), - UNIT_DEREGISTRATION_ACKNOWLEDGE( "DE_REGIST_ACK ", "De-Registration Acknowledge", 47 ), - TDMA_SYNC_BROADCAST( "TDMA_SYNC_BCST ", "FDMA-TDMA Sync Broadcast", 48 ), - RESERVED_31( "RESERVED_31 ", "Reserved", 49 ), - RESERVED_32( "RESERVED_32 ", "Reserved", 50 ), - IDENTIFIER_UPDATE_TDMA( "IDEN_UPDATE_TDMA", "Identifier Update for TDMA", 51 ), - IDENTIFIER_UPDATE_VHF_UHF_BANDS( "IDEN_UPDATE_VUHF", "Identifier Update for VHF/UHF Bands", 52 ), - TIME_DATE_ANNOUNCEMENT( "TIME_DATE_ANNOUN", "Time and Date Announcement", 53 ), - ROAMING_ADDRESS_COMMAND( "ROAM_ADDR_CMD ", "Roaming Address Command", 54 ), - ROAMING_ADDRESS_UPDATE( "ROAM_ADDR_UPDATE", "Roaming Address Update", 55 ), - SYSTEM_SERVICE_BROADCAST( "SYS_SVC_BCAST ", "System Service Broadcast", 56 ), - SECONDARY_CONTROL_CHANNEL_BROADCAST( "SEC_CCH_BROADCST", "Secondary Control Channel Broadcast", 57 ), - RFSS_STATUS_BROADCAST( "RFSS_STATUS_BCST", "RFSS Status Broadcast", 58 ), - NETWORK_STATUS_BROADCAST( "NET_STATUS_BCAST", "Network Status Broadcast", 59 ), - ADJACENT_STATUS_BROADCAST( "ADJ SITE STATUS ", "Adjacent Site Status Broadcast", 60 ), - IDENTIFIER_UPDATE_NON_VUHF( "IDEN_UPDATE ", "Identifier Update", 61 ), - PROTECTION_PARAMETER_BROADCAST( "ENCRYPT_PAR_BCST", "Protection Parameter Broadcast", 62 ), - PROTECTION_PARAMETER_UPDATE( "ENCRYPT_PAR_UPDT", "Protection Parameter Update", 63 ), - UNKNOWN( "UNKNOWN ", "Unknown", -1 ); - - private String mLabel; - private String mDescription; - private int mCode; - - private Opcode( String label, String description, int code ) - { - mLabel = label; - mDescription = description; - mCode = code; - } - - public String getLabel() - { - return mLabel; - } - - public String getDescription() - { - return mDescription; - } - - public int getCode() - { - return mCode; - } - - public static Opcode fromValue( int value ) - { - if( 0 <= value && value <= 63 ) - { - return values()[ value ]; - } - - return UNKNOWN; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUFormat.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUFormat.java index f10155b7..b27a2d98 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUFormat.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUFormat.java @@ -2,66 +2,71 @@ package io.github.dsheirer.module.decode.p25.reference; public enum PDUFormat { - F00( "F00", 0 ), - F01( "F01", 1 ), - F02( "F02", 2 ), - F03( "F03", 3 ), - F04( "F04", 4 ), - F05( "F05", 5 ), - F06( "F06", 6 ), - F07( "F07", 7 ), - F08( "F08", 8 ), - F09( "F09", 9 ), - F10( "F10", 10 ), - F11( "F11", 11 ), - F12( "F12", 12 ), - F13( "F13", 13 ), - F14( "F14", 14 ), - F15( "F15", 15 ), - F16( "F16", 16 ), - F17( "F17", 17 ), - F18( "F18", 18 ), - F19( "F19", 19 ), - F20( "F20", 20 ), - UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL( "UNCONFIRMED MBTC", 21 ), - PACKET_DATA( "PACKET DATA", 22 ), - ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL( "ALTERNATE MBTC", 23 ), - F24( "F24", 24 ), - F25( "F25", 25 ), - F26( "F26", 26 ), - F27( "F27", 27 ), - F28( "F28", 28 ), - F29( "F29", 29 ), - F30( "F30", 30 ), - F31( "F31", 31 ), - UNKNOWN( "UNKN", -1 ); - - private String mLabel; - private int mValue; - - private PDUFormat( String label, int value ) - { - mLabel = label; - mValue = value; - } - - public String getLabel() - { - return mLabel; - } - - public int getValue() - { - return mValue; - } - - public static PDUFormat fromValue( int value ) - { - if( 0 <= value && value <= 31 ) - { - return values()[ value ]; - } - - return UNKNOWN; - } + F00("F00", 0), + F01("F01", 1), + F02("F02", 2), + RESPONSE_PACKET_HEADER_FORMAT("RESPONSE", 3), + F04("F04", 4), + F05("F05", 5), + F06("F06", 6), + F07("F07", 7), + F08("F08", 8), + F09("F09", 9), + F10("F10", 10), + F11("F11", 11), + F12("F12", 12), + F13("F13", 13), + F14("F14", 14), + F15("F15", 15), + F16("F16", 16), + F17("F17", 17), + F18("F18", 18), + F19("F19", 19), + F20("F20", 20), + UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL("UNCONFIRMED MBTC", 21), + PACKET_DATA("PACKET DATA", 22), + ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL("ALTERNATE MBTC", 23), + F24("F24", 24), + F25("F25", 25), + F26("F26", 26), + F27("F27", 27), + F28("F28", 28), + F29("F29", 29), + F30("F30", 30), + F31("F31", 31), + UNKNOWN("UNKN", -1); + + private String mLabel; + private int mValue; + + PDUFormat(String label, int value) + { + mLabel = label; + mValue = value; + } + + public String getLabel() + { + return mLabel; + } + + public int getValue() + { + return mValue; + } + + public static PDUFormat fromValue(int value) + { + if(0 <= value && value <= 31) + { + return values()[value]; + } + + return UNKNOWN; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUPriorityMaximumType.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUPriorityMaximumType.java new file mode 100644 index 00000000..4cb4df12 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUPriorityMaximumType.java @@ -0,0 +1,65 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +public enum PDUPriorityMaximumType +{ + PRI_0("LOW-0"), + PRI_1("MED-1"), + PRI_2("MED-2"), + PRI_3("MED-3"), + PRI_4("MED-4"), + PRI_5("MED-5"), + PRI_6("MED-6"), + PRI_7("MED-7"), + PRI_8("MED-8"), + PRI_9("MED-9"), + PRI_10("MED-10"), + PRI_11("MED-11"), + PRI_12("MED-12"), + PRI_13("MED-13"), + PRI_14("HIGH-14"), + PRI_15("EMERGENCY-15"), + UNKNOWN("UNKNOWN"); + + private String mLabel; + + PDUPriorityMaximumType(String label) + { + mLabel = label; + } + + @Override + public String toString() + { + return mLabel; + } + + public static PDUPriorityMaximumType fromValue(int value) + { + if(0 <= value && value <= 15) + { + return PDUPriorityMaximumType.values()[value]; + } + + return UNKNOWN; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUType.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUType.java index 2066dc25..993a5a4b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUType.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PDUType.java @@ -2,55 +2,83 @@ package io.github.dsheirer.module.decode.p25.reference; public enum PDUType { - /* Outbound */ - SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-ACTIVATE TDS CONTEXT ACCEPT", 0 ), - SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-DEACTIVATE TDS CONTEXT ACCEPT", 1 ), - SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST( "SNDCP-DEACTIVATE TDS CONTEXT REQUEST", 2 ), - SNDCP_ACTIVATE_TDS_CONTEXT_REJECT( "SNDCP-ACTIVATE TDS CONTEXT REJECT", 3 ), - SNDCP_RF_UNCONFIRMED_DATA( "SNDCP-RF UNCONFIRMED DATA", 4 ), - SNDCP_RF_CONFIRMED_DATA( "SNDCP-RF CONFIRMED DATA", 5 ), - PDU_TYPE_6( "PDU TYPE 6 UNKNOWN", 6 ), - PDU_TYPE_7( "PDU TYPE 7 UNKNOWN", 7 ), - PDU_TYPE_8( "PDU TYPE 8 UNKNOWN", 8 ), - PDU_TYPE_9( "PDU TYPE 9 UNKNOWN", 9 ), - PDU_TYPE_10( "PDU TYPE 10 UNKNOWN", 10 ), - PDU_TYPE_11( "PDU TYPE 11 UNKNOWN", 11 ), - - /* Inbound */ - SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST( "SNDCP-ACTIVATE TDS CONTEXT REQUEST", 0 ), + /* Outbound */ + OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT("ACTIVATE TDS CONTEXT ACCEPT", 0), + OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT("DEACTIVATE TDS CONTEXT ACCEPT", 1), + OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST("DEACTIVATE TDS CONTEXT REQUEST", 2), + OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REJECT("ACTIVATE TDS CONTEXT REJECT", 3), + OUTBOUND_SNDCP_RF_UNCONFIRMED_DATA("RF UNCONFIRMED DATA", 4), + OUTBOUND_SNDCP_RF_CONFIRMED_DATA("RF CONFIRMED DATA", 5), + OUTBOUND_UNKNOWN("OUTBOUND UNKNOWN PDU TYPE", -1), - UNKNOWN( "UNKNOWN", -1 ); - - private String mLabel; - private int mValue; - - private PDUType( String label, int value ) - { - mLabel = label; - mValue = value; - } - - public String getLabel() - { - return mLabel; - } - - public int getValue() - { - return mValue; - } - - public static PDUType fromValue( int value, boolean outbound ) - { - if( outbound && 0 <= value && value <= 11 ) - { - return values()[ value ]; - } - else if( value == 0 && !outbound ) - { - return SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST; - } - - return UNKNOWN; - } + INBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST("ACTIVATE TDS CONTEXT REQUEST", 0), + INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT("DEACTIVATE TDS CONTEXT ACCEPT", 1), + INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST("DEACTIVATE TDS CONTEXT REQUEST", 2), + INBOUND_SNDCP_RF_CONFIRMED_DATA("RF CONFIRMED DATA", 5), + INBOUND_UNKNOWN("INBOUND UNKNOWN PDU TYPE", -1); + + private String mLabel; + private int mValue; + + PDUType(String label, int value) + { + mLabel = label; + mValue = value; + } + + @Override + public String toString() + { + return mLabel; + } + + public String getLabel() + { + return mLabel; + } + + public int getValue() + { + return mValue; + } + + public static PDUType fromValue(int value, boolean outbound) + { + if(outbound) + { + switch(value) + { + case 0: + return OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT; + case 1: + return OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT; + case 2: + return OUTBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST; + case 3: + return OUTBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REJECT; + case 4: + return OUTBOUND_SNDCP_RF_UNCONFIRMED_DATA; + case 5: + return OUTBOUND_SNDCP_RF_CONFIRMED_DATA; + default: + return OUTBOUND_UNKNOWN; + } + } + else + { + switch(value) + { + case 0: + return INBOUND_SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST; + case 1: + return INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT; + case 2: + return INBOUND_SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST; + case 5: + return INBOUND_SNDCP_RF_CONFIRMED_DATA; + default: + return INBOUND_UNKNOWN; + } + } + } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/PacketResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PacketResponse.java new file mode 100644 index 00000000..61ebf74a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/PacketResponse.java @@ -0,0 +1,51 @@ +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Response Packet responses + */ +public enum PacketResponse +{ + ALL_BLOCKS_SUCCESSFULLY_RECEIVED("ALL BLOCKS RECEIVED", 0x08), + ILLEGAL_FORMAT("ILLEGAL FORMAT", 0x40), + PACKET_CRC_FAIL("PACKET CRC-32 FAILED", 0x48), + MEMORY_FULL("MEMORY FULL", 0x50), + FSN_OUT_OF_SEQUENCE("FSN OUT OF SEQUENCE", 0x58), + UNDELIVERABLE("UNDELIVERABLE", 0x60), + MSN_OUT_OF_SEQUENCE("MSG OUT OF SEQUENCE", 0x68), + UNAUTHORIZED_USER("UNAUTHORIZED USER", 0x70), + SELECTIVE_RETRY("SELECTIVE RETRY", 0x80), + UNKNOWN("UNKNOWN", 0xFF); + + private String mLabel; + private int mMask; + + PacketResponse(String label, int mask) + { + mLabel = label; + mMask = mask; + } + + public static PacketResponse fromValue(int value) + { + for(PacketResponse packetResponse : values()) + { + if((value & packetResponse.getMask()) == packetResponse.getMask()) + { + return packetResponse; + } + } + + return UNKNOWN; + } + + private int getMask() + { + return mMask; + } + + @Override + public String toString() + { + return mLabel; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ReadyTimer.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ReadyTimer.java new file mode 100644 index 00000000..6945907f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ReadyTimer.java @@ -0,0 +1,64 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +public enum ReadyTimer +{ + SECONDS_RESERVED(0), + SECONDS_1(1), + SECONDS_2(2), + SECONDS_4(4), + SECONDS_6(6), + SECONDS_8(8), + SECONDS_10(10), + SECONDS_15(15), + SECONDS_20(20), + SECONDS_25(25), + SECONDS_30(30), + SECONDS_60(60), + SECONDS_120(120), + SECONDS_180(180), + SECONDS_300(300), + SECONDS_ALWAYS(86400), //Infinity + UNKNOWN(0); + + private int mSeconds; + + ReadyTimer(int seconds) + { + mSeconds = seconds; + } + + public int getSeconds() + { + return mSeconds; + } + + public static ReadyTimer fromValue(int value) + { + if(0 <= value && value <= 15) + { + return ReadyTimer.values()[value]; + } + + return UNKNOWN; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/SNDCPActivationRejectReason.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/RejectReason.java similarity index 85% rename from src/main/java/io/github/dsheirer/module/decode/p25/reference/SNDCPActivationRejectReason.java rename to src/main/java/io/github/dsheirer/module/decode/p25/reference/RejectReason.java index 722da47f..a464974a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/SNDCPActivationRejectReason.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/RejectReason.java @@ -1,6 +1,6 @@ package io.github.dsheirer.module.decode.p25.reference; -public enum SNDCPActivationRejectReason +public enum RejectReason { R0( 0, "ANY REASON" ), R1( 1, "MRC NOT PROVISIONED FOR TDS" ), @@ -20,7 +20,7 @@ public enum SNDCPActivationRejectReason private int mValue; public String mLabel; - private SNDCPActivationRejectReason( int value, String label ) + private RejectReason(int value, String label ) { mValue = value; mLabel = label; @@ -41,7 +41,7 @@ public enum SNDCPActivationRejectReason return getLabel(); } - public static SNDCPActivationRejectReason fromValue( int value ) + public static RejectReason fromValue(int value ) { if( 0 <= value && value <= 12 ) { diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceAccessPoint.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceAccessPoint.java index e133aeb3..9c6e33d9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceAccessPoint.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceAccessPoint.java @@ -1,99 +1,119 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.p25.reference; public enum ServiceAccessPoint { - UNENCRYPTED_USER_DATA( "USER DATA", 0 ), - ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ), - CIRCUIT_DATA( "CIRCUIT DATA", 2 ), - CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ), - PACKET_DATA( "PACKET DATA", 4 ), - ADDRESS_RESOLUTION_PROTOCOL( "ADDRESS RESOLUTION PROTOCOL", 5 ), - SNDCP_PACKET_DATA_CONTROL( "SNDCP PACKET DATA CONTROL", 6 ), - SAP_7( "7", 7 ), - SAP_8( "8", 8 ), - SAP_9( "9", 9 ), - SAP_10( "10", 10 ), - SAP_11( "11", 11 ), - SAP_12( "12", 12 ), - SAP_13( "13", 13 ), - SAP_14( "14", 14 ), - SAP_15( "15", 15 ), - SAP_16( "16", 16 ), - SAP_17( "17", 17 ), - SAP_18( "18", 18 ), - SAP_19( "19", 19 ), - SAP_20( "20", 20 ), - SAP_21( "21", 21 ), - SAP_22( "22", 22 ), - SAP_23( "23", 23 ), - SAP_24( "24", 24 ), - SAP_25( "25", 25 ), - SAP_26( "26", 26 ), - SAP_27( "27", 27 ), - SAP_28( "28", 28 ), - SAP_29( "29", 29 ), - SAP_30( "30", 30 ), - EXTENDED_ADDRESS( "EXTENDED ADDRESS", 31 ), - REGISTRATION_AND_AUTHORIZATION( "REGISTRATION AND AUTHORIZATION", 32 ), - CHANNEL_REASSIGNMENT( "CHANNEL REASSIGNMENT", 33 ), - SYSTEM_CONFIGURATION( "SYSTEM CONFIGURATION", 34 ), - MR_LOOPBACK( "MOBILE RADIO LOOPBACK", 35 ), - MR_STATISTICS( "MOBILE RADIO STATISTICS", 36 ), - MR_OUT_OF_SERVICE( "MOBILE RADIO OUT OF SERVICE", 37 ), - MR_PAGING( "MOBILE RADIO PAGING", 38 ), - MR_CONFIGURATION( "MOBILE RADIO CONFIGURATION", 39 ), - UNENCRYPTED_KEY_MANAGEMENT_MESSAGE( "UNENCRYPTED KEY MANAGEMENT", 40 ), - ENCRYPTED_KEY_MANAGEMENT_MESSAGE( "ENCRYPTED KEY MANAGEMENT", 41 ), - SAP_42( "42", 42 ), - SAP_43( "43", 43 ), - SAP_44( "44", 44 ), - SAP_45( "45", 45 ), - SAP_46( "46", 46 ), - SAP_47( "47", 47 ), - SAP_48( "48", 48 ), - SAP_49( "49", 49 ), - SAP_50( "50", 50 ), - SAP_51( "51", 51 ), - SAP_52( "52", 52 ), - SAP_53( "53", 53 ), - SAP_54( "54", 54 ), - SAP_55( "55", 55 ), - SAP_56( "56", 56 ), - SAP_57( "57", 57 ), - SAP_58( "58", 58 ), - SAP_59( "59", 59 ), - SAP_60( "60", 60 ), - UNENCRYPTED_TRUNKING_CONTROL( "TRUNKING CONTROL", 61 ), - SAP_62( "62", 62 ), - ENCRYPTED_TRUNKING_CONTROL( "ENCRYPTED TRUNKING CONTROL", 63 ), - UKNOWN( "UNKN", -1 ); + UNENCRYPTED_USER_DATA("USER DATA", 0), + ENCRYPTED_USER_DATA("ENCRYPTED USER DATA", 1), + CIRCUIT_DATA("CIRCUIT DATA", 2), + CIRCUIT_DATA_CONTROL("CIRCUIT DATA CONTROL", 3), + PACKET_DATA("PACKET DATA", 4), + ADDRESS_RESOLUTION_PROTOCOL("ADDRESS RESOLUTION PROTOCOL", 5), + SNDCP_PACKET_DATA_CONTROL("SNDCP PACKET DATA CONTROL", 6), + SAP_7("7", 7), + SAP_8("8", 8), + SAP_9("9", 9), + SAP_10("10", 10), + SAP_11("11", 11), + SAP_12("12", 12), + SAP_13("13", 13), + SAP_14("14", 14), + SAP_15("15", 15), + SAP_16("16", 16), + SAP_17("17", 17), + SAP_18("18", 18), + SAP_19("19", 19), + SAP_20("20", 20), + SAP_21("21", 21), + SAP_22("22", 22), + SAP_23("23", 23), + SAP_24("24", 24), + SAP_25("25", 25), + SAP_26("26", 26), + SAP_27("27", 27), + SAP_28("28", 28), + SAP_29("29", 29), + SAP_30("30", 30), + EXTENDED_ADDRESS("EXTENDED ADDRESS", 31), + REGISTRATION_AND_AUTHORIZATION("REGISTRATION AND AUTHORIZATION", 32), + CHANNEL_REASSIGNMENT("CHANNEL REASSIGNMENT", 33), + SYSTEM_CONFIGURATION("SYSTEM CONFIGURATION", 34), + MOBILE_RADIO_LOOPBACK("MOBILE RADIO LOOPBACK", 35), + MOBILE_RADIO_STATISTICS("MOBILE RADIO STATISTICS", 36), + MOBILE_RADIO_OUT_OF_SERVICE("MOBILE RADIO OUT OF SERVICE", 37), + MOBILE_RADIO_PAGING("MOBILE RADIO PAGING", 38), + MOBILE_RADIO_CONFIGURATION("MOBILE RADIO CONFIGURATION", 39), + UNENCRYPTED_KEY_MANAGEMENT_MESSAGE("UNENCRYPTED KEY MANAGEMENT", 40), + ENCRYPTED_KEY_MANAGEMENT_MESSAGE("ENCRYPTED KEY MANAGEMENT", 41), + SAP_42("42", 42), + SAP_43("43", 43), + SAP_44("44", 44), + SAP_45("45", 45), + SAP_46("46", 46), + SAP_47("47", 47), + SAP_48("48", 48), + SAP_49("49", 49), + SAP_50("50", 50), + SAP_51("51", 51), + SAP_52("52", 52), + SAP_53("53", 53), + SAP_54("54", 54), + SAP_55("55", 55), + SAP_56("56", 56), + SAP_57("57", 57), + SAP_58("58", 58), + SAP_59("59", 59), + SAP_60("60", 60), + UNENCRYPTED_TRUNKING_CONTROL("TRUNKING CONTROL", 61), + SAP_62("62", 62), + ENCRYPTED_TRUNKING_CONTROL("ENCRYPTED TRUNKING CONTROL", 63), + UNKNOWN("UNKN", -1); - private String mLabel; - private int mValue; - - private ServiceAccessPoint( String label, int value ) - { - mLabel = label; - mValue = value; - } - - public String getLabel() - { - return mLabel; - } - - public int getValue() - { - return mValue; - } - - public static ServiceAccessPoint fromValue( int value ) - { - if( 0 <= value && value <= 63 ) - { - return values()[ value ]; - } - - return UKNOWN; - } + private String mLabel; + private int mValue; + + private ServiceAccessPoint(String label, int value) + { + mLabel = label; + mValue = value; + } + + public String getLabel() + { + return mLabel; + } + + public int getValue() + { + return mValue; + } + + public static ServiceAccessPoint fromValue(int value) + { + if(0 <= value && value <= 63) + { + return values()[value]; + } + + return UNKNOWN; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceOptions.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceOptions.java new file mode 100644 index 00000000..4e1ee1d4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/ServiceOptions.java @@ -0,0 +1,99 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Channel allocation service options + */ +public class ServiceOptions +{ + private static final int EMERGENCY_FLAG = 0x80; + private static final int ENCRYPTION_FLAG = 0x40; + private static final int DUPLEX = 0x20; + private static final int SESSION_MODE = 0x10; + + protected int mServiceOptions; + + public ServiceOptions(int serviceOptions) + { + mServiceOptions = serviceOptions; + } + + /** + * Indicates if the channel is specially processed as an emergency service. + */ + public boolean isEmergency() + { + return isSet(EMERGENCY_FLAG); + } + + /** + * Indicates if the channel is encrypted for the session. + */ + public boolean isEncrypted() + { + return isSet(ENCRYPTION_FLAG); + } + + /** + * Duplex mode for the session. Half duplex indicates that only one entity can communicate + * at a time. Full duplex indicates that both entities can communicate simultaneously. + */ + public Duplex getDuplex() + { + return isSet(DUPLEX) ? Duplex.FULL : Duplex.HALF; + } + + /** + * Network mode for the session, packet (data) or circuit (voice). + */ + public SessionMode getSessionMode() + { + return isSet(SESSION_MODE) ? SessionMode.PACKET : SessionMode.CIRCUIT; + } + + /** + * Indicates if this session mode is for packet + */ + public boolean isPacket() + { + return getSessionMode() == SessionMode.PACKET; + } + + /** + * Indicates if this session mode is for voice + */ + public boolean isVoice() + { + return getSessionMode() == SessionMode.CIRCUIT; + } + + /** + * Indicates if the bit(s) referenced by the mask is set in the service options value. + * + * @param mask to check for set bits + * @return true if the masked bit value(s) are all set. + */ + private boolean isSet(int mask) + { + return (mServiceOptions & mask) == mask; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/SessionMode.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/SessionMode.java new file mode 100644 index 00000000..a7ad1765 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/SessionMode.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.reference; + +public enum SessionMode +{ + PACKET, CIRCUIT +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/StandbyTimer.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/StandbyTimer.java new file mode 100644 index 00000000..d20a85f6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/StandbyTimer.java @@ -0,0 +1,64 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +public enum StandbyTimer +{ + SECONDS_RESERVED(0), + SECONDS_10(10), + SECONDS_30(30), + MINUTES_1(60), + MINUTES_5(600), + MINUTES_10(1200), + MINUTES_30(1800), + HOURS_1(3600), + HOURS_2(7200), + HOURS_4(14400), + HOURS_8(28800), + HOURS_12(43200), + HOURS_24(86400), + HOURS_48(172800), + HOURS_72(259200), + ALWAYS(1000000), //Infinity + UNKNOWN(0); + + private int mSeconds; + + StandbyTimer(int seconds) + { + mSeconds = seconds; + } + + public int getSeconds() + { + return mSeconds; + } + + public static StandbyTimer fromValue(int value) + { + if(0 <= value && value <= 15) + { + return StandbyTimer.values()[value]; + } + + return UNKNOWN; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Status.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Status.java index 0e4bc163..5f3b6a8b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Status.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Status.java @@ -2,20 +2,25 @@ package io.github.dsheirer.module.decode.p25.reference; public enum Status { - TALKAROUND, - BUSY, - REPEATER, - IDLE, - UNKNOWN; - - - public Status fromValue( int value ) - { - if( 0 <= value && value <= 3 ) - { - return values()[ value ]; - } - - return UNKNOWN; - } + //Used by subscriber radios for repeated or direct connect communications + MOBILE_RADIO, + + //Outbound repeater, inbound channel is busy + REPEATER_BUSY, + + //Subscriber or repeater, unknown status + UNKNOWN, + + //Outbound repeater, inbound channel is idle + REPEATER_IDLE; + + public Status fromValue(int value) + { + if(0 <= value && value <= 3) + { + return values()[value]; + } + + return UNKNOWN; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/UDPHeaderCompression.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/UDPHeaderCompression.java new file mode 100644 index 00000000..7ed14e24 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/UDPHeaderCompression.java @@ -0,0 +1,56 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +public enum UDPHeaderCompression +{ + NONE( 0, "NONE" ), + UNKNOWN( -1, "UNKNOWN" ); + + private int mValue; + private String mLabel; + + UDPHeaderCompression(int value, String label ) + { + mValue = value; + mLabel = label; + } + + public int getValue() + { + return mValue; + } + + public String getLabel() + { + return mLabel; + } + + public static UDPHeaderCompression fromValue(int value ) + { + if(value == 0) + { + return NONE; + } + + return UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/Vocoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Vocoder.java new file mode 100644 index 00000000..01778346 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/Vocoder.java @@ -0,0 +1,30 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.reference; + +/** + * Vocoder data rates + */ +public enum Vocoder +{ + FULL_RATE, + HALF_RATE; +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/VoiceServiceOptions.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/VoiceServiceOptions.java new file mode 100644 index 00000000..6761dbce --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/VoiceServiceOptions.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * sdr-trunk + * 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 + * + ******************************************************************************/ +package io.github.dsheirer.module.decode.p25.reference; + +public class VoiceServiceOptions extends ServiceOptions +{ + private static final int PRIORITY = 0x07; + + public VoiceServiceOptions(int serviceOptions) + { + super(serviceOptions); + } + + /** + * Priority, 1 - 7, assigned to the channel for the session. + * + * 0 = Reserved + * 1 = Lowest priority + * ... + * 4 = Default + * ... + * 7 = Highest priority + * + * @return priority for the session + */ + public int getPriority() + { + return (mServiceOptions & PRIORITY); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("PRI").append(getPriority()); + + if(isEncrypted()) + { + sb.append(" ENCRYPTED"); + } + + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + + sb.append(" ").append(getSessionMode()); + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportCallEvent.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportCallEvent.java deleted file mode 100644 index 40fc6b12..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportCallEvent.java +++ /dev/null @@ -1,164 +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 - ******************************************************************************/ -package io.github.dsheirer.module.decode.passport; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; - -public class PassportCallEvent extends CallEvent -{ - private String mChannel; - private long mFrequency; - - public PassportCallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String channel, - long frequency, - String details ) - { - super( DecoderType.PASSPORT, callEventType, - aliasList, fromID, toID, details ); - - mChannel = channel; - mFrequency = frequency; - } - - private Alias getAlias(String talkgroup ) - { - if( hasAliasList() ) - { - return getAliasList().getTalkgroupAlias( talkgroup ); - } - - return null; - } - - private Alias getMobileIDAlias( String mobileID ) - { - if( hasAliasList() ) - { - return getAliasList().getMobileIDNumberAlias( mobileID ); - } - - return null; - } - - @Override - public Alias getFromIDAlias() - { - return getMobileIDAlias( getFromID() ); - } - - @Override - public Alias getToIDAlias() - { - return getAlias( getToID() ); - } - - @Override - public String getChannel() - { - return mChannel; - } - - @Override - public long getFrequency() - { - return mFrequency; - } - - public static class Builder - { - /* Required parameters */ - private CallEventType mCallEventType; - - /* Optional parameters */ - private AliasList mAliasList; - private String mFromID; - private String mToID; - private String mDetails; - private String mChannel; - private long mFrequency; - - public Builder( CallEventType callEventType ) - { - mCallEventType = callEventType; - } - - public Builder aliasList( AliasList aliasList ) - { - mAliasList = aliasList; - return this; - } - - public Builder from( String val ) - { - mFromID = val; - return this; - } - - public Builder channel( String channel ) - { - mChannel = channel; - return this; - } - - public Builder details( String details ) - { - mDetails = details; - return this; - } - - public Builder frequency( long frequency ) - { - mFrequency = frequency; - return this; - } - - public Builder to( String toID ) - { - mToID = toID; - return this; - } - - public PassportCallEvent build() - { - return new PassportCallEvent( this ); - } - - } - - /** - * Private constructor for the builder - */ - private PassportCallEvent( Builder builder ) - { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mChannel, - builder.mFrequency, - builder.mDetails ); - } - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java index ab94e76a..d9336b5a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.passport; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.bits.SyncPattern; import io.github.dsheirer.dsp.fsk.LTRDecoder; @@ -38,7 +41,7 @@ public class PassportDecoder extends Decoder implements IReusableBufferListener, * Passport Decoder. Decodes unfiltered (e.g. demodulated but with no DC or * audio filtering) samples and produces Passport messages. */ - public PassportDecoder(DecodeConfiguration config, AliasList aliasList) + public PassportDecoder(DecodeConfiguration config) { mLTRDecoder = new LTRDecoder(PASSPORT_MESSAGE_LENGTH); @@ -46,7 +49,7 @@ public class PassportDecoder extends Decoder implements IReusableBufferListener, mLTRDecoder.setMessageFramer(mPassportMessageFramer); mPassportMessageFramer.setSyncDetectListener(mLTRDecoder); - mPassportMessageProcessor = new PassportMessageProcessor(aliasList); + mPassportMessageProcessor = new PassportMessageProcessor(); mPassportMessageFramer.addMessageListener(mPassportMessageProcessor); mPassportMessageProcessor.setMessageListener(getMessageListener()); } diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java index 805278ec..7a47bad8 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,23 +15,25 @@ * * 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.passport; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.message.MessageType; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.passport.identifier.PassportTalkgroup; +import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,34 +42,30 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.TreeSet; public class PassportDecoderState extends DecoderState { private final static Logger mLog = LoggerFactory.getLogger(PassportDecoderState.class); - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mMobileIDs = new TreeSet(); - private HashMap mSiteLCNs = new HashMap(); - private HashMap mNeighborLCNs = new HashMap(); - - private AliasedStringAttributeMonitor mFromMobileIDAttribute; - private AliasedStringAttributeMonitor mToTalkgroupAttribute; + private Set mTalkgroupsFirstHeard = new HashSet<>(); + private Set mTalkgroups = new TreeSet<>(); + private Set mMobileIDs = new TreeSet<>(); + private Map mSiteLCNs = new HashMap<>(); + private Map mNeighborLCNs = new HashMap<>(); + private Map mDetectedCalls = new HashMap<>(); + private DecodeEvent mCurrentDecodeEvent; private int mChannelNumber; private int mSiteNumber; private PassportBand mSiteBand; - private HashMap mActiveCalls = new HashMap(); private long mFrequency; - public PassportDecoderState(AliasList aliasList) + public PassportDecoderState() { - super(aliasList); - - mFromMobileIDAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MIN); - mToTalkgroupAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); } @Override @@ -75,7 +74,7 @@ public class PassportDecoderState extends DecoderState return DecoderType.PASSPORT; } - private void logTalkgroup(String talkgroup) + private void addTalkgroup(PassportTalkgroup talkgroup) { if(mTalkgroupsFirstHeard.contains(talkgroup)) { @@ -87,265 +86,149 @@ public class PassportDecoderState extends DecoderState } } - private PassportCallEvent getCurrentCallEvent() + /** + * Compares the talkgroups for equality + */ + private boolean isSameTalkgroup(Identifier id1, Identifier id2) { - return (PassportCallEvent) mCurrentCallEvent; + return Objects.equals(id1, id2); } /** - * Indicates if the talkgroup is different than the talkgroup specified in - * the current call event + * Retrieves the first identifier with a TO role. + * + * @param collection containing a TO identifier + * @return TO identifier or null */ - private boolean isDifferentTalkgroup(String talkgroup) + private Identifier getToIdentifier(IdentifierCollection collection) { - return talkgroup != null && - mCurrentCallEvent != null && - mCurrentCallEvent.getToID() != null && - !mCurrentCallEvent.getToID().contentEquals(talkgroup); + List identifiers = collection.getIdentifiers(Role.TO); + + if(identifiers.size() >= 1) + { + return identifiers.get(0); + } + + return null; } + @Override - public void receive(Message message) + public void receive(IMessage message) { - if(message instanceof PassportMessage) + if(message instanceof PassportMessage && message.isValid()) { - PassportMessage passport = (PassportMessage) message; + PassportMessage passport = (PassportMessage)message; if(passport.isValid()) { switch(passport.getMessageType()) { case CA_STRT: - mSiteLCNs.put(passport.getLCN(), - passport.getLCNFrequencyFormatted()); + case DA_STRT: + mSiteLCNs.put(passport.getLCN(), passport.getLCNFrequency()); - String talkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(talkgroup); + PassportTalkgroup to = passport.getToIdentifier(); + addTalkgroup(to); if(mChannelNumber == 0) { setChannelNumber(passport.getLCN()); } - /* Call on this channel */ + /* Call on this channel */ if(passport.getLCN() == mChannelNumber) { - mToTalkgroupAttribute.process(talkgroup); + getIdentifierCollection().update(to); - PassportCallEvent current = getCurrentCallEvent(); - - /* If we're already in a call event, add the message - * to the current call event ... if false, then we - * have a different call ... cleanup the old one. */ - if(current != null && isDifferentTalkgroup(talkgroup)) + if(mCurrentDecodeEvent != null) { - mCurrentCallEvent.end(); - mCurrentCallEvent = null; + if(isSameTalkgroup(getToIdentifier(mCurrentDecodeEvent.getIdentifierCollection()), to)) + { + mCurrentDecodeEvent.update(passport.getTimestamp()); + broadcast(mCurrentDecodeEvent); + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); + return; + } + else + { + mCurrentDecodeEvent.end(message.getTimestamp()); + broadcast(mCurrentDecodeEvent); + mCurrentDecodeEvent = null; + } } - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new PassportCallEvent - .Builder(CallEvent.CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(passport.getLCNFrequency()) - .to(String.valueOf(passport.getTalkgroupID())) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, - Event.START, State.CALL)); - } - else - { - broadcast(new DecoderStateEvent(this, - Event.CONTINUATION, State.CALL)); - } + mCurrentDecodeEvent = DecodeEvent.builder(passport.getTimestamp()) + .eventDescription(passport.getMessageType() == MessageType.CA_STRT ? "Call" : "Data") + .identifiers(getIdentifierCollection().copyOf()) + .details(passport.toString()) + .build(); + broadcast(mCurrentDecodeEvent); + broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); } else { //Call Detection int lcn = passport.getLCN(); - String tg = String.valueOf(passport.getTalkgroupID()); - if(!mActiveCalls.containsKey(lcn) || - !mActiveCalls.get(lcn).contentEquals(tg)) + DecodeEvent callDetect = mDetectedCalls.get(lcn); + + if(callDetect == null || + !isSameTalkgroup(to, getToIdentifier(callDetect.getIdentifierCollection())) || + callDetect.getTimeStart() < (passport.getTimestamp() - 45000)) { - mActiveCalls.put(lcn, tg); - - broadcast(new PassportCallEvent - .Builder(CallEvent.CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(String.valueOf(lcn)) - .details("Site: " + passport.getSite()) - .frequency(passport.getLCNFrequency()) - .to(tg) - .build()); + callDetect = DecodeEvent.builder(passport.getTimestamp()) + .eventDescription("Call Detect") + .identifiers(new IdentifierCollection(passport.getIdentifiers())) +// .channel(...) + .build(); + mDetectedCalls.put(lcn, callDetect); } + else + { + callDetect.update(passport.getTimestamp()); + } + + broadcast(callDetect); } break; case CA_ENDD: - String endTalkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(endTalkgroup); - mToTalkgroupAttribute.process(endTalkgroup); - - broadcast(new DecoderStateEvent(this, - Event.END, State.CALL)); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - mCurrentCallEvent = null; - - broadcast(new DecoderStateEvent(this, Event.RESET, State.FADE)); - } - break; - case DA_STRT: - mSiteLCNs.put(passport.getLCN(), - passport.getLCNFrequencyFormatted()); - - String dataTalkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(dataTalkgroup); - - if(mChannelNumber == 0) - { - setChannelNumber(passport.getLCN()); - } - - /* Data call on this channel */ - if(passport.getLCN() == mChannelNumber) - { - mToTalkgroupAttribute.process(dataTalkgroup); - - PassportCallEvent current = getCurrentCallEvent(); - - /* If we're already in a call event, add the message - * to the current call event ... if false, then we - * have a different call ... cleanup the old one. */ - if(current != null && isDifferentTalkgroup(dataTalkgroup)) - { - mCurrentCallEvent.end(); - mCurrentCallEvent = null; - } - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new PassportCallEvent - .Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(passport.getLCNFrequency()) - .to(String.valueOf(passport.getTalkgroupID())) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, - Event.START, State.DATA)); - } - else - { - broadcast(new DecoderStateEvent(this, - Event.CONTINUATION, State.DATA)); - } - } - else - { - //Call Detection - int lcn = passport.getLCN(); - String tg = String.valueOf(passport.getTalkgroupID()); - - if(!mActiveCalls.containsKey(lcn) || - !mActiveCalls.get(lcn).contentEquals(tg)) - { - mActiveCalls.put(lcn, tg); - - broadcast(new PassportCallEvent - .Builder(CallEvent.CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(lcn)) - .details("Site: " + passport.getSite()) - .frequency(passport.getLCNFrequency()) - .to(tg) - .build()); - } - } - break; case DA_ENDD: - String dataEndTalkgroup = - String.valueOf(passport.getTalkgroupID()); + PassportTalkgroup endTalkgroup = passport.getToIdentifier(); + addTalkgroup(endTalkgroup); - logTalkgroup(dataEndTalkgroup); - mToTalkgroupAttribute.process(dataEndTalkgroup); - - broadcast(new DecoderStateEvent(this, - Event.END, State.DATA)); - - if(mCurrentCallEvent != null) + if(mCurrentDecodeEvent != null && isSameTalkgroup(endTalkgroup, getToIdentifier(mCurrentDecodeEvent.getIdentifierCollection()))) { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - mCurrentCallEvent = null; - - broadcast(new DecoderStateEvent(this, Event.RESET, State.FADE)); + mCurrentDecodeEvent.end(passport.getTimestamp()); + mCurrentDecodeEvent = null; } + + broadcast(new DecoderStateEvent(this, Event.END, State.CALL)); break; case ID_RDIO: - String min = passport.getMobileID(); + PassportTalkgroup mobileId = passport.getFromIdentifier(); + mMobileIDs.add(mobileId); + getIdentifierCollection().update(mobileId); - if(min != null) + if(mCurrentDecodeEvent != null) { - mMobileIDs.add(min); - } - - mFromMobileIDAttribute.process(min); - - final CallEvent current = getCurrentCallEvent(); - - if(current != null) - { - current.setFromID(min); - broadcast(current); - + mCurrentDecodeEvent.setIdentifierCollection(getIdentifierCollection().copyOf()); + mCurrentDecodeEvent.update(passport.getTimestamp()); + broadcast(mCurrentDecodeEvent); } break; case RA_REGI: - if(mCurrentCallEvent == null || - mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.REGISTER) - { - mCurrentCallEvent = new PassportCallEvent - .Builder(CallEvent.CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(passport.getLCN())) - .frequency(passport.getLCNFrequency()) - .to(passport.getToID()) - .build(); + broadcast(DecodeEvent.builder(passport.getTimestamp()) + .eventDescription("Register") + .identifiers(new IdentifierCollection(passport.getIdentifiers())) + .build()); - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, - Event.START, State.DATA)); - } - else - { - broadcast(new DecoderStateEvent(this, - Event.CONTINUATION, State.DATA)); - } + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); break; case SY_IDLE: if(passport.getFree() != 0) { - mNeighborLCNs.put(passport.getFree(), - passport.getFreeFrequencyFormatted()); + mNeighborLCNs.put(passport.getFree(), passport.getFreeFrequency()); } setSiteNumber(passport.getSite()); @@ -360,26 +243,6 @@ public class PassportDecoderState extends DecoderState } } - public static String formatTalkgroup(String talkgroup) - { - StringBuilder sb = new StringBuilder(); - - if(talkgroup.length() == 6) - { - sb.append(talkgroup.substring(0, 1)); - sb.append("-"); - sb.append(talkgroup.substring(1, 3)); - sb.append("-"); - sb.append(talkgroup.substring(3, 6)); - - return sb.toString(); - } - else - { - return talkgroup; - } - } - @Override public String getActivitySummary() { @@ -395,7 +258,7 @@ public class PassportDecoderState extends DecoderState } else { - ArrayList channels = new ArrayList<>(mSiteLCNs.keySet()); + List channels = new ArrayList<>(mSiteLCNs.keySet()); Collections.sort(channels); for(Integer channel : channels) @@ -414,7 +277,7 @@ public class PassportDecoderState extends DecoderState } else { - ArrayList channels = new ArrayList<>(mNeighborLCNs.keySet()); + List channels = new ArrayList<>(mNeighborLCNs.keySet()); Collections.sort(channels); for(Integer channel : channels) @@ -433,27 +296,11 @@ public class PassportDecoderState extends DecoderState } else { - Iterator it = mTalkgroups.iterator(); + Iterator it = mTalkgroups.iterator(); while(it.hasNext()) { - String tgid = it.next(); - - sb.append(" "); - sb.append(tgid); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(tgid); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); + sb.append(" ").append(it.next()).append("\n"); } } @@ -465,27 +312,11 @@ public class PassportDecoderState extends DecoderState } else { - Iterator it = mMobileIDs.iterator(); + Iterator it = mMobileIDs.iterator(); while(it.hasNext()) { - String min = it.next(); - - sb.append(" "); - sb.append(min); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMobileIDNumberAlias(min); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); + sb.append(" ").append(it.next()).append("\n"); } } @@ -494,6 +325,7 @@ public class PassportDecoderState extends DecoderState public void reset() { + getIdentifierCollection().remove(IdentifierClass.USER); mTalkgroupsFirstHeard.clear(); mTalkgroups.clear(); mMobileIDs.clear(); @@ -521,10 +353,15 @@ public class PassportDecoderState extends DecoderState } - private void resetState() + protected void resetState() { - mToTalkgroupAttribute.reset(); - mFromMobileIDAttribute.reset(); + super.resetState(); + + if(mCurrentDecodeEvent != null) + { + mCurrentDecodeEvent.end(System.currentTimeMillis()); + mCurrentDecodeEvent = null; + } } public int getChannelNumber() @@ -537,7 +374,8 @@ public class PassportDecoderState extends DecoderState if(mChannelNumber != channel) { mChannelNumber = channel; - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, "CHAN:" + mChannelNumber)); + getIdentifierCollection().update(DecoderLogicalChannelNameIdentifier.create(String.valueOf(mChannelNumber), + Protocol.PASSPORT)); } } @@ -551,8 +389,6 @@ public class PassportDecoderState extends DecoderState if(mSiteNumber != site) { mSiteNumber = site; - Alias alias = hasAliasList() ? getAliasList().getSiteID(String.valueOf(getSiteNumber())) : null; - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_1, "SITE:" + mSiteNumber, alias)); } } @@ -566,7 +402,6 @@ public class PassportDecoderState extends DecoderState if(mSiteBand == null) { mSiteBand = band; - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_2, "BAND:" + band.getDescription())); } } @@ -580,7 +415,6 @@ public class PassportDecoderState extends DecoderState break; case SOURCE_FREQUENCY: mFrequency = event.getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, mFrequency)); break; default: break; diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java index cfaf1331..f67c6520 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java @@ -1,123 +1,152 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.passport; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.CRC; import io.github.dsheirer.edac.CRCPassport; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; import io.github.dsheirer.message.MessageType; -import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.module.decode.passport.identifier.PassportTalkgroup; +import io.github.dsheirer.protocol.Protocol; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; public class PassportMessage extends Message { - private static final String sUNKNOWN = "**UNKNOWN**"; - private SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - @SuppressWarnings( "unused" ) - private static final int[] sSYNC = { 8,7,6,5,4,3,2,1,0 }; - private static final int[] sDCC = { 10,9 }; //Digital Color Code - private static final int[] sLCN = { 21,20,19,18,17,16,15,14,13,12,11 }; - private static final int[] sSITE = { 28,27,26,25,24,23,22 }; - private static final int[] sGROUP = { 44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29 }; - private static final int[] sRADIO_ID = { 44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22 }; -// private static final int[] sNEIGHBOR_OFFSET = { 32,31,30 }; - private static final int[] sNEIGHBOR_BAND = { 36,35,34,33 }; - private static final int[] sNEIGHBOR_REGISTRATION = { 29 }; - private static final int[] sSITE_OFFSET = { 40,39,38 }; - private static final int[] sSITE_BAND = { 44,43,42,41 }; - private static final int[] sSITE_REGISTRATION = { 37 }; - private static final int[] sTYPE = { 48,47,46,45 }; - private static final int[] sFREE = { 59,58,57,56,55,54,53,52,51,50,49 }; - private static final int[] sCHECKSUM = { 67,66,65,64,63,62,61,60 }; - private BinaryMessage mMessage; + private static final int[] SYNC = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + private static final int[] DIGITAL_COLOR_CODE = {9, 10}; + private static final int[] CHANNEL_NUMBER = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; + private static final int[] SITE = {22, 23, 24, 25, 26, 27, 28}; + private static final int[] GROUP = {29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44}; + private static final int[] RADIO_ID = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44}; + private static final int[] NEIGHBOR_BAND = {33, 34, 35, 36}; + private static final int[] SITE_BAND = {41, 42, 43, 44}; + private static final int[] TYPE = {45, 46, 47, 48}; + private static final int[] FREE = {49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}; + private static final int[] CHECKSUM = {60, 61, 62, 63, 64, 65, 66, 67}; + + private CorrectedBinaryMessage mMessage; private CRC mCRC; - private AliasList mAliasList; - private PassportMessage mIdleMessage; - - public PassportMessage( BinaryMessage message, - PassportMessage idleMessage, - AliasList list ) + private PassportMessage mIdleMessage; + private PassportTalkgroup mFromIdentifier; + private PassportTalkgroup mToIdentifier; + private List mIdentifiers; + + public PassportMessage(CorrectedBinaryMessage message, PassportMessage idleMessage) { - mMessage = CRCPassport.correct( message ); + mMessage = CRCPassport.correct(message); mIdleMessage = idleMessage; - mCRC = CRCPassport.check( mMessage ); - mAliasList = list; + mCRC = CRCPassport.check(mMessage); } - - public PassportMessage( BinaryMessage message, AliasList list ) + + public PassportMessage(CorrectedBinaryMessage message) { - this( message, null, list ); + this(message, null); } - - public BinaryMessage getBitSetBuffer() + + public CorrectedBinaryMessage getMessage() { - return mMessage; + return mMessage; } - + + public PassportTalkgroup getToIdentifier() + { + if(mToIdentifier == null) + { + mToIdentifier = PassportTalkgroup.createTo(getMessage().getInt(GROUP)); + } + + return mToIdentifier; + } + + public PassportTalkgroup getFromIdentifier() + { + if(mFromIdentifier == null) + { + mFromIdentifier = PassportTalkgroup.createFrom(getMessage().getInt(RADIO_ID)); + } + + return mFromIdentifier; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + + if(hasFromIdentifier()) + { + mIdentifiers.add(getFromIdentifier()); + } + + if(hasToIdentifier()) + { + mIdentifiers.add(getToIdentifier()); + } + } + + return mIdentifiers; + } + public boolean isValid() { - return mCRC != CRC.FAILED_CRC && - mCRC != CRC.FAILED_PARITY && - mCRC != CRC.UNKNOWN; + return mCRC.passes(); } - + public CRC getCRC() { - return mCRC; + return mCRC; } public MessageType getMessageType() { MessageType retVal = MessageType.UN_KNWN; - + int type = getMessageTypeNumber(); int lcn = getLCN(); - - switch( type ) + + switch(type) { case 0: //Group Call retVal = MessageType.CA_STRT; - break; + break; case 1: - if( getFree() == 2042 ) - { - retVal = MessageType.ID_TGAS; - } - else if( lcn < 1792 ) + if(getFree() == 2042) { - retVal = MessageType.CA_STRT; + retVal = MessageType.ID_TGAS; } - else if( lcn == 1792 || lcn == 1793 ) + else if(lcn < 1792) + { + retVal = MessageType.CA_STRT; + } + else if(lcn == 1792 || lcn == 1793) { retVal = MessageType.SY_IDLE; } - else if( lcn == 2047 ) + else if(lcn == 2047) { retVal = MessageType.CA_ENDD; } @@ -126,513 +155,226 @@ public class PassportMessage extends Message retVal = MessageType.CA_STRT; break; case 5: - retVal = MessageType.CA_PAGE; - break; + retVal = MessageType.CA_PAGE; + break; case 6: - retVal = MessageType.ID_RDIO; - break; + retVal = MessageType.ID_RDIO; + break; case 9: - retVal = MessageType.DA_STRT; - break; + retVal = MessageType.DA_STRT; + break; case 11: - retVal = MessageType.RA_REGI; - break; + retVal = MessageType.RA_REGI; + break; default: break; } - + return retVal; } - - - public int getDCC() + + + public int getColorCode() { - return getInt( sDCC ); + return getMessage().getInt(DIGITAL_COLOR_CODE); } - + public int getSite() { - return getInt( sSITE ); + return getMessage().getInt(SITE); } - + public int getMessageTypeNumber() { - return getInt( sTYPE ); + return getMessage().getInt(TYPE); } - - public int getTalkgroupID() - { - if( getMessageType() == MessageType.SY_IDLE ) - { - return 0; - } - else - { - return getInt( sGROUP ); - } - } - - public Alias getTalkgroupIDAlias() - { - int tg = getTalkgroupID(); - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( String.valueOf( tg ) ); - } - - return null; + public boolean hasToIdentifier() + { + return getMessageType() != MessageType.SY_IDLE; } - + public int getLCN() { - return getInt( sLCN ); + return getMessage().getInt(CHANNEL_NUMBER); } public long getLCNFrequency() { - return getSiteFrequency( getLCN() ); + return getSiteFrequency(getLCN()); } - - public String getLCNFrequencyFormatted() - { - return mDecimalFormatter.format( (double)getLCNFrequency() / 1000000.0d ); - } - + public PassportBand getSiteBand() { - return PassportBand.lookup( getInt( sSITE_BAND ) ); + return PassportBand.lookup(getMessage().getInt(SITE_BAND)); } public PassportBand getNeighborBand() { - return PassportBand.lookup( getInt( sNEIGHBOR_BAND ) ); + return PassportBand.lookup(getMessage().getInt(NEIGHBOR_BAND)); } public int getFree() { - return getInt( sFREE ); + return getMessage().getInt(FREE); } - + public long getFreeFrequency() { - return getSiteFrequency( getFree() ); + return getSiteFrequency(getFree()); } - - public String getFreeFrequencyFormatted() - { - return mDecimalFormatter.format( (double)getFreeFrequency() / 1000000.0d ); - } - + public long getNeighborFrequency() { - if( getMessageType() == MessageType.SY_IDLE ) - { - PassportBand band = getNeighborBand(); - - return band.getFrequency( getFree() ); - } - - return 0; + if(getMessageType() == MessageType.SY_IDLE) + { + PassportBand band = getNeighborBand(); + + return band.getFrequency(getFree()); + } + + return 0; } - + + public boolean hasFromIdentifier() + { + return getMessageType() == MessageType.ID_RDIO; + } + /** - * Returns the radio id in hex format, or null if not the correct message - * type - * - * @return - radio id or null + * Appends spaces to the end of the stringbuilder to make it length long */ - public String getMobileID() + private void pad(StringBuilder sb, int length) { - if( getMessageType() == MessageType.ID_RDIO ) - { - int radioId = getInt( sRADIO_ID ); - - return String.format("%06X", radioId & 0xFFFFFF ); - } - else - { - return null; - } - } - - public Alias getMobileIDAlias() - { - String min = getMobileID(); - - if( mAliasList != null && min != null ) - { - return mAliasList.getMobileIDNumberAlias( min ); - } - - return null; - } - - /** - * Appends spaces to the end of the stringbuilder to make it length long - */ - private void pad( StringBuilder sb, int length ) - { - while( sb.length() < length ) - { - sb.append( " " ); - } - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); + while(sb.length() < length) + { + sb.append(" "); + } } - private int getInt( int[] bits ) - { - int retVal = 0; - - for( int x = 0; x < bits.length; x++ ) - { - if( mMessage.get( bits[ x ] ) ) - { - retVal += 1< 0 && free < 896 ) - { - sb.append( "/" ); - sb.append( getFreeFrequencyFormatted() ); - } - sb.append( " TYP:" ); - sb.append( format( getMessageTypeNumber(), 2 ) ); - sb.append( " TG:" ); - sb.append( format( getTalkgroupID(), 5 ) ); - break; - } - - return sb.toString(); - } - - @Override + @Override public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append( mSDF.format( mTimeReceived ) ); - sb.append( " " ); - sb.append( getMessage() ); - pad( sb, 100 ); - sb.append( mCRC.toString() ); - pad( sb, 110 ); - sb.append( " [" ); - sb.append( mMessage ); - sb.append( "]" ); + StringBuilder sb = new StringBuilder(); + sb.append("DCC:").append(getColorCode()); - return sb.toString(); + switch(getMessageType()) + { + case SY_IDLE: + sb.append(" IDLE SITE:").append(format(getSite(), 3)); + sb.append(" NEIGHBOR:").append(format(getFree(), 3)).append("/").append(getFreeFrequency()); + break; + case CA_PAGE: + sb.append(" PAGING TG:").append(getToIdentifier()); + sb.append(" SITE:").append(format(getSite(), 3)); + sb.append(" CHAN:").append(format(getLCN(), 4)).append("/").append(getLCNFrequency()); + sb.append(" FREE:").append(format(getFree(), 3)).append("/").append(getFreeFrequency()); + break; + case CA_STRT: + sb.append(" CALL TG:").append(getToIdentifier()); + sb.append(" SITE:").append(format(getSite(), 3)); + sb.append(" CHAN:").append(format(getLCN(), 4)).append("/").append(getLCNFrequency()); + sb.append(" FREE:").append(format(getFree(), 3)).append("/").append(getFreeFrequency()); + break; + case DA_STRT: + sb.append(" ** DATA TG:").append(getToIdentifier()); + sb.append(" SITE:").append(format(getSite(), 3)); + sb.append(" CHAN:").append(format(getLCN(), 4)).append("/").append(getLCNFrequency()); + sb.append(" FREE:").append(format(getFree(), 3)).append("/").append(getFreeFrequency()); + break; + case CA_ENDD: + sb.append(" END TG:").append(getToIdentifier()); + sb.append(" SITE:").append(format(getSite(), 3)); + sb.append(" CHAN:").append(format(getLCN(), 4)).append("/").append(getLCNFrequency()); + sb.append(" FREE:").append(format(getFree(), 3)).append("/").append(getFreeFrequency()); + break; + case ID_RDIO: + sb.append(" MOBILE ID MIN:").append(getFromIdentifier()); + sb.append(" FREE:").append(format(getFree(), 3)).append("/").append(getFreeFrequency()); + break; + case ID_TGAS: + sb.append(" ASSIGN TALKGROUP:").append(getToIdentifier()); + sb.append(" SITE:").append(format(getSite(), 3)); + sb.append(" CHAN:").append(format(getLCN(), 4)).append("/").append(getLCNFrequency()); + break; + case RA_REGI: + sb.append(" RADIO REGISTER TG: ").append(getToIdentifier()); + break; + default: + sb.append(" UNKNOWN SITE:").append(format(getSite(), 3)); + sb.append(" CHAN:").append(format(getLCN(), 4)).append("/").append(getLCNFrequency()); + sb.append(" FREE:"); + int free = getFree(); + sb.append(format(free, 3)); + if(free > 0 && free < 896) + { + sb.append("/"); + sb.append(getFreeFrequency()); + } + sb.append(" TYP:").append(format(getMessageTypeNumber(), 2)); + sb.append(" TG:").append(getToIdentifier()); + break; + } + + return sb.toString(); } - @Override - public String getErrorStatus() + public boolean matches(PassportMessage otherMessage) { - return mCRC.getDisplayText(); + return this.getMessage().equals(otherMessage.getMessage()); } - - public boolean matches( PassportMessage otherMessage ) - { - return this.getBitSetBuffer().equals( otherMessage.getBitSetBuffer() ); - } - - @Override - public Plottable getPlottable() - { - // TODO Auto-generated method stub - return null; - } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias talkgroup = getTalkgroupIDAlias(); - - if( talkgroup != null ) - { - aliases.add( talkgroup ); - } - - Alias mid = getMobileIDAlias(); - - if( mid != null ) - { - aliases.add( mid ); - } - - return aliases; - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java index ee98e88a..f58b086f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java @@ -2,67 +2,58 @@ package io.github.dsheirer.module.decode.passport; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.MessageType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; -public class PassportMessageFilter extends Filter +public class PassportMessageFilter extends Filter { - private HashMap> mElements = - new HashMap>(); - - public PassportMessageFilter() - { - super( "Passport Message Filter" ); + private Map> mElements = new HashMap>(); - mElements.put( MessageType.CA_STRT, - new FilterElement( MessageType.CA_STRT ) ); - mElements.put( MessageType.CA_ENDD, - new FilterElement( MessageType.CA_ENDD ) ); - mElements.put( MessageType.SY_IDLE, - new FilterElement( MessageType.SY_IDLE ) ); - mElements.put( MessageType.ID_TGAS, - new FilterElement( MessageType.ID_TGAS ) ); - mElements.put( MessageType.ID_ESNH, - new FilterElement( MessageType.ID_ESNH ) ); - mElements.put( MessageType.CA_PAGE, - new FilterElement( MessageType.CA_PAGE ) ); - mElements.put( MessageType.ID_RDIO, - new FilterElement( MessageType.ID_RDIO ) ); - mElements.put( MessageType.DA_STRT, - new FilterElement( MessageType.DA_STRT ) ); - mElements.put( MessageType.RA_REGI, - new FilterElement( MessageType.RA_REGI ) ); - } - - @Override - public boolean passes( Message message ) + public PassportMessageFilter() { - if( mEnabled && canProcess( message ) ) - { - PassportMessage passport = (PassportMessage)message; - - if( mElements.containsKey( passport.getMessageType() ) ) - { - return mElements.get( passport.getMessageType() ).isEnabled(); - } - } - - return false; + super("Passport Message Filter"); + + mElements.put(MessageType.CA_STRT, new FilterElement(MessageType.CA_STRT)); + mElements.put(MessageType.CA_ENDD, new FilterElement(MessageType.CA_ENDD)); + mElements.put(MessageType.SY_IDLE, new FilterElement(MessageType.SY_IDLE)); + mElements.put(MessageType.ID_TGAS, new FilterElement(MessageType.ID_TGAS)); + mElements.put(MessageType.ID_ESNH, new FilterElement(MessageType.ID_ESNH)); + mElements.put(MessageType.CA_PAGE, new FilterElement(MessageType.CA_PAGE)); + mElements.put(MessageType.ID_RDIO, new FilterElement(MessageType.ID_RDIO)); + mElements.put(MessageType.DA_STRT, new FilterElement(MessageType.DA_STRT)); + mElements.put(MessageType.RA_REGI, new FilterElement(MessageType.RA_REGI)); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean passes(IMessage message) { - return message instanceof PassportMessage; + if(mEnabled && canProcess(message)) + { + PassportMessage passport = (PassportMessage) message; + + if(mElements.containsKey(passport.getMessageType())) + { + return mElements.get(passport.getMessageType()).isEnabled(); + } + } + + return false; } - @Override + @Override + public boolean canProcess(IMessage message) + { + return message instanceof PassportMessage; + } + + @Override public List> getFilterElements() { - return new ArrayList>( mElements.values() ); + return new ArrayList>(mElements.values()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageProcessor.java index ddce69b7..23820cd3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageProcessor.java @@ -1,111 +1,114 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.passport; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.MessageType; import io.github.dsheirer.sample.Listener; import java.util.HashMap; +import java.util.Map; -public class PassportMessageProcessor implements Listener +public class PassportMessageProcessor implements Listener { - private Listener mMessageListener; - private IdleMessageFinder mIdleFinder = new IdleMessageFinder(); - private AliasList mAliasList; - private PassportMessage mIdleMessage; - - public PassportMessageProcessor( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( BinaryMessage buffer ) - { - if( mMessageListener != null ) - { - PassportMessage message; - - if( mIdleMessage != null ) - { - message = new PassportMessage( buffer, mIdleMessage, mAliasList ); - } - else - { - message = new PassportMessage( buffer, mAliasList ); - mIdleFinder.receive( message ); - } + private Listener mMessageListener; + private IdleMessageFinder mIdleFinder = new IdleMessageFinder(); + private PassportMessage mIdleMessage; - mMessageListener.receive( message ); - } - } - - public void setMessageListener( Listener listener ) + public PassportMessageProcessor() { - mMessageListener = listener; + } + + @Override + public void receive(CorrectedBinaryMessage buffer) + { + if(mMessageListener != null) + { + PassportMessage message; + + if(mIdleMessage != null) + { + message = new PassportMessage(buffer, mIdleMessage); + } + else + { + message = new PassportMessage(buffer); + mIdleFinder.receive(message); + } + + if(message.isValid()) + { + mMessageListener.receive(message); + } + + } + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; + } + + public class IdleMessageFinder + { + public Map mMessageCounts = new HashMap(); + + public boolean mIdleMessageFound = false; + + public IdleMessageFinder() + { + } + + public void receive(PassportMessage message) + { + if(!mIdleMessageFound && + message.isValid() && + message.getMessageType() == MessageType.SY_IDLE) + { + if(mMessageCounts.containsKey(message.toString())) + { + int count = mMessageCounts.get(message.toString()); + + if(count >= 3) + { + mIdleMessageFound = true; + mIdleMessage = message; + mMessageCounts = null; + } + else + { + count++; + mMessageCounts.put(message.toString(), count); + } + } + else + { + mMessageCounts.put(message.toString(), 1); + } + } + } } - - public class IdleMessageFinder - { - public HashMap mMessageCounts = - new HashMap(); - - public boolean mIdleMessageFound = false; - - public IdleMessageFinder() - { - } - - public void receive( PassportMessage message ) - { - if( !mIdleMessageFound && - message.isValid() && - message.getMessageType() == MessageType.SY_IDLE ) - { - if( mMessageCounts.containsKey( message.getBinaryMessage() ) ) - { - int count = mMessageCounts.get( message.getBinaryMessage() ); - - if( count >= 3 ) - { - mIdleMessageFound = true; - mIdleMessage = message; - mMessageCounts = null; - } - else - { - count++; - mMessageCounts.put( message.getBinaryMessage(), count ); - } - } - else - { - mMessageCounts.put( message.getBinaryMessage(), 1 ); - } - } - } - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/identifier/PassportTalkgroup.java b/src/main/java/io/github/dsheirer/module/decode/passport/identifier/PassportTalkgroup.java new file mode 100644 index 00000000..e5a0324c --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/passport/identifier/PassportTalkgroup.java @@ -0,0 +1,67 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.passport.identifier; + +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Passport Talkgroup + */ +public class PassportTalkgroup extends TalkgroupIdentifier implements Comparable +{ + /** + * Constructs a Passport talkgroup with a TO role. + */ + public PassportTalkgroup(Integer value, Role role, boolean isGroup) + { + super(value, role, isGroup); + } + + @Override + public Protocol getProtocol() + { + return Protocol.PASSPORT; + } + + /** + * Creates a Passport talkgroup with the value as the TO role + */ + public static PassportTalkgroup createTo(int value) + { + return new PassportTalkgroup(value, Role.TO, true); + } + + /** + * Creates a Passport talkgroup (ie mobile id) with the value as the TO role + */ + public static PassportTalkgroup createFrom(int value) + { + return new PassportTalkgroup(value, Role.FROM, false); + } + + @Override + public int compareTo(PassportTalkgroup o) + { + return Integer.compare(getValue(), o.getValue()); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessage.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessage.java index 9bcc471d..24ecf553 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessage.java @@ -1,28 +1,30 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.tait; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.module.decode.tait.identifier.TaitIdentifier; +import io.github.dsheirer.protocol.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,241 +34,133 @@ import java.util.List; public class Tait1200ANIMessage extends Message { - private final static Logger mLog = LoggerFactory.getLogger( Tait1200ANIMessage.class ); + private final static Logger mLog = LoggerFactory.getLogger(Tait1200ANIMessage.class); - public static int[] REVS_1 = { 0,1,2,3 }; - public static int[] SYNC = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }; - public static int[] SIZE = { 20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35 }; - public static int[] FROM_DIGIT_1 = { 36,37,38,39,40,41,42,43 }; - public static int[] FROM_DIGIT_2 = { 44,45,46,47,48,49,50,51 }; - public static int[] FROM_DIGIT_3 = { 52,53,54,55,56,57,58,59 }; - public static int[] FROM_DIGIT_4 = { 60,61,62,63,64,65,66,67 }; - public static int[] FROM_DIGIT_5 = { 68,69,70,71,72,73,74,75 }; - public static int[] FROM_DIGIT_6 = { 76,77,78,79,80,81,82,83 }; - public static int[] FROM_DIGIT_7 = { 84,85,86,87,88,89,90,91 }; - public static int[] FROM_DIGIT_8 = { 92,93,94,95,96,97,98,99 }; - public static int[] CHECKSUM_1 = { 100,101,102,103,104,105,106,107,108,109, - 110,111,112,113,114,115 }; + public static int[] REVS_1 = {0, 1, 2, 3}; + public static int[] SYNC = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + public static int[] SIZE = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + public static int[] FROM_DIGIT_1 = {36, 37, 38, 39, 40, 41, 42, 43}; + public static int[] FROM_DIGIT_2 = {44, 45, 46, 47, 48, 49, 50, 51}; + public static int[] FROM_DIGIT_3 = {52, 53, 54, 55, 56, 57, 58, 59}; + public static int[] FROM_DIGIT_4 = {60, 61, 62, 63, 64, 65, 66, 67}; + public static int[] FROM_DIGIT_5 = {68, 69, 70, 71, 72, 73, 74, 75}; + public static int[] FROM_DIGIT_6 = {76, 77, 78, 79, 80, 81, 82, 83}; + public static int[] FROM_DIGIT_7 = {84, 85, 86, 87, 88, 89, 90, 91}; + public static int[] FROM_DIGIT_8 = {92, 93, 94, 95, 96, 97, 98, 99}; + public static int[] CHECKSUM_1 = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115}; - public static int[] REVS_2 = { 116,117,118,119,120,121,122,123,124,125,126,127, - 128,129,130,131 }; - public static int[] SIZE_2 = { 188,189,190,191,192,193,194,195,196,197,198, - 199,200,201,202,203 }; - public static int[] TO_DIGIT_1 = { 204,205,206,207,208,209,210,211 }; - public static int[] TO_DIGIT_2 = { 212,213,214,215,216,217,218,219 }; - public static int[] TO_DIGIT_3 = { 220,221,222,223,224,225,226,227 }; - public static int[] TO_DIGIT_4 = { 228,229,230,231,232,233,234,235 }; - public static int[] TO_DIGIT_5 = { 236,237,238,239,240,241,242,243 }; - public static int[] TO_DIGIT_6 = { 244,245,246,247,248,249,250,251 }; - public static int[] TO_DIGIT_7 = { 252,253,254,255,256,257,258,259 }; - public static int[] TO_DIGIT_8 = { 260,261,262,263,264,265,266,267 }; - public static int[] UNKNOWN_1 = { 268,269,270,271,272,273,274,275 }; - public static int[] CHECKSUM_2 = { 276,277,278,279,280,281,282,283,284,285, - 286,287,288,289,290,291 }; + public static int[] REVS_2 = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131}; + public static int[] SIZE_2 = {188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203}; + public static int[] TO_DIGIT_1 = {204, 205, 206, 207, 208, 209, 210, 211}; + public static int[] TO_DIGIT_2 = {212, 213, 214, 215, 216, 217, 218, 219}; + public static int[] TO_DIGIT_3 = {220, 221, 222, 223, 224, 225, 226, 227}; + public static int[] TO_DIGIT_4 = {228, 229, 230, 231, 232, 233, 234, 235}; + public static int[] TO_DIGIT_5 = {236, 237, 238, 239, 240, 241, 242, 243}; + public static int[] TO_DIGIT_6 = {244, 245, 246, 247, 248, 249, 250, 251}; + public static int[] TO_DIGIT_7 = {252, 253, 254, 255, 256, 257, 258, 259}; + public static int[] TO_DIGIT_8 = {260, 261, 262, 263, 264, 265, 266, 267}; + public static int[] UNKNOWN_1 = {268, 269, 270, 271, 272, 273, 274, 275}; + public static int[] CHECKSUM_2 = {276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291}; - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); + private static SimpleDateFormat mSDF = new SimpleDateFormat("yyyyMMdd HHmmss"); - private BinaryMessage mMessage; - private AliasList mAliasList; + private CorrectedBinaryMessage mMessage; private CRC mCRC; - - public Tait1200ANIMessage( BinaryMessage message, AliasList list ) + private TaitIdentifier mFromIdentifier; + private TaitIdentifier mToIdentifier; + private List mIdentifiers; + + public Tait1200ANIMessage(CorrectedBinaryMessage message) { - mMessage = message; - mAliasList = list; - -// checkCRC(); -// -// switch( mCRC ) -// { -// case CORRECTED: -// mLog.debug( "CORR:" + message.toString() ); -// break; -// case FAILED_CRC: -// mLog.debug( "FAIL:" + message.toString() ); -// break; -// case PASSED: -// mLog.debug( "PASS:" + message.toString() ); -// break; -// } + mMessage = message; } - - @Override - public String getFromID() + + public TaitIdentifier getFromIdentifier() { - StringBuilder sb = new StringBuilder(); - - sb.append( getCharacter( FROM_DIGIT_1 ) ); - sb.append( getCharacter( FROM_DIGIT_2 ) ); - sb.append( getCharacter( FROM_DIGIT_3 ) ); - sb.append( getCharacter( FROM_DIGIT_4 ) ); - sb.append( getCharacter( FROM_DIGIT_5 ) ); - sb.append( getCharacter( FROM_DIGIT_6 ) ); - sb.append( getCharacter( FROM_DIGIT_7 ) ); - sb.append( getCharacter( FROM_DIGIT_8 ) ); - - return sb.toString(); + if(mFromIdentifier == null) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getCharacter(FROM_DIGIT_1)); + sb.append(getCharacter(FROM_DIGIT_2)); + sb.append(getCharacter(FROM_DIGIT_3)); + sb.append(getCharacter(FROM_DIGIT_4)); + sb.append(getCharacter(FROM_DIGIT_5)); + sb.append(getCharacter(FROM_DIGIT_6)); + sb.append(getCharacter(FROM_DIGIT_7)); + sb.append(getCharacter(FROM_DIGIT_8)); + + mFromIdentifier = TaitIdentifier.createFrom(sb.toString().trim()); + } + + return mFromIdentifier; } - - @Override - public Alias getFromIDAlias() + + public TaitIdentifier getToIdentifier() { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getFromID() ); - } - - return null; + if(mToIdentifier == null) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getCharacter(TO_DIGIT_1)); + sb.append(getCharacter(TO_DIGIT_2)); + sb.append(getCharacter(TO_DIGIT_3)); + sb.append(getCharacter(TO_DIGIT_4)); + sb.append(getCharacter(TO_DIGIT_5)); + sb.append(getCharacter(TO_DIGIT_6)); + sb.append(getCharacter(TO_DIGIT_7)); + sb.append(getCharacter(TO_DIGIT_8)); + + mToIdentifier = TaitIdentifier.createTo(sb.toString().trim()); + } + + return mToIdentifier; } - - @Override - public String getToID() + + public char getCharacter(int[] bits) { - StringBuilder sb = new StringBuilder(); - - sb.append( getCharacter( TO_DIGIT_1 ) ); - sb.append( getCharacter( TO_DIGIT_2 ) ); - sb.append( getCharacter( TO_DIGIT_3 ) ); - sb.append( getCharacter( TO_DIGIT_4 ) ); - sb.append( getCharacter( TO_DIGIT_5 ) ); - sb.append( getCharacter( TO_DIGIT_6 ) ); - sb.append( getCharacter( TO_DIGIT_7 ) ); - sb.append( getCharacter( TO_DIGIT_8 ) ); - - return sb.toString(); - } - - @Override - public Alias getToIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getToID() ); - } - - return null; - } - - public char getCharacter( int[] bits ) - { - int value = mMessage.getInt( bits ); - - return (char)value; + int value = mMessage.getInt(bits); + + return (char)value; } public boolean isValid() { // return mCRC == CRC.PASSED || mCRC == CRC.CORRECTED; - return true; + return true; } - + @Override public String toString() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( "ANI FROM:" ); - sb.append( getFromID() ); - sb.append( " TO:" ); - sb.append( getToID() ); + sb.append("ANI FROM:").append(getFromIdentifier()); + sb.append(" TO:").append(getToIdentifier()); - return sb.toString(); + return sb.toString(); } - /** - * Pads spaces onto the end of the value to make it 'places' long - */ - public String pad( String value, int places, String padCharacter ) + @Override + public Protocol getProtocol() { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); - } - - /** - * Pads an integer value with additional zeroes to make it decimalPlaces long - */ - public String format( int number, int decimalPlaces ) - { - StringBuilder sb = new StringBuilder(); - - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); - - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } - - @Override - public String getProtocol() - { - return "Tait-1200"; + return Protocol.TAIT1200; } - @Override - public String getEventType() + @Override + public List getIdentifiers() { - return "ANI"; - } + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getFromIdentifier()); + mIdentifiers.add(getToIdentifier()); + } - @Override - public String getMessage() - { - return toString(); + return mIdentifiers; } - - @Override - public String getErrorStatus() - { - return "UNKNOWN"; -// return mCRC.getDisplayText(); - } - - @Override - public Plottable getPlottable() - { - return null; - } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - return aliases; - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessageProcessor.java index d384ad28..382c26c9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200ANIMessageProcessor.java @@ -1,65 +1,61 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.tait; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.Message; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Tait1200ANIMessageProcessor implements Listener +public class Tait1200ANIMessageProcessor implements Listener { - private final static Logger mLog = - LoggerFactory.getLogger( Tait1200ANIMessageProcessor.class ); + private final static Logger mLog = LoggerFactory.getLogger(Tait1200ANIMessageProcessor.class); + private Listener mMessageListener; - private Listener mMessageListener; - - private AliasList mAliasList; - - public Tait1200ANIMessageProcessor( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mMessageListener = null; - mAliasList = null; - } - - @Override - public void receive( BinaryMessage buffer ) + public Tait1200ANIMessageProcessor() { - if( mMessageListener != null ) - { - mMessageListener.receive( new Tait1200ANIMessage( buffer, mAliasList ) ); - } - } - - public void setMessageListener( Listener listener ) - { - mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void dispose() { - mMessageListener = null; + mMessageListener = null; + } + + @Override + public void receive(CorrectedBinaryMessage buffer) + { + if(mMessageListener != null) + { + mMessageListener.receive(new Tait1200ANIMessage(buffer)); + } + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; + } + + public void removeMessageListener(Listener listener) + { + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200CallEvent.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200CallEvent.java index e6a53d70..5cbf898c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200CallEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200CallEvent.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.tait; import io.github.dsheirer.alias.Alias; @@ -24,36 +26,31 @@ import io.github.dsheirer.module.decode.event.CallEvent; public class Tait1200CallEvent extends CallEvent { - public Tait1200CallEvent( CallEventType callEventType, - AliasList aliasList, - String fromID, - String toID, - String details ) + public Tait1200CallEvent(CallEventType callEventType, + AliasList aliasList, + String fromID, + String toID, + String details) { - super( DecoderType.TAIT_1200, callEventType, - aliasList, fromID, toID, details ); + super(DecoderType.TAIT_1200, callEventType, + aliasList, fromID, toID, details); } - private Alias getAlias( String ident ) + private Alias getAlias(String ident) { - if( hasAliasList() ) - { - return getAliasList().getFleetsyncAlias( ident ); - } - return null; } - + @Override public Alias getFromIDAlias() { - return getAlias( getFromID() ); + return getAlias(getFromID()); } @Override public Alias getToIDAlias() { - return getAlias( getToID() ); + return getAlias(getToID()); } @Override @@ -79,30 +76,30 @@ public class Tait1200CallEvent extends CallEvent private String mToID; private String mDetails; - public Builder( CallEventType callEventType ) + public Builder(CallEventType callEventType) { mCallEventType = callEventType; } - - public Builder aliasList( AliasList aliasList ) + + public Builder aliasList(AliasList aliasList) { mAliasList = aliasList; return this; } - - public Builder from( String val ) + + public Builder from(String val) { mFromID = val; return this; -} + } - public Builder details( String details ) + public Builder details(String details) { mDetails = details; return this; } - - public Builder to( String toID ) + + public Builder to(String toID) { mToID = toID; return this; @@ -110,28 +107,22 @@ public class Tait1200CallEvent extends CallEvent public Tait1200CallEvent build() { - return new Tait1200CallEvent( this ); + return new Tait1200CallEvent(this); } } /** * Private constructor for the builder */ - private Tait1200CallEvent( Builder builder ) + private Tait1200CallEvent(Builder builder) { - this( builder.mCallEventType, - builder.mAliasList, - builder.mFromID, - builder.mToID, - builder.mDetails ); + this(builder.mCallEventType, builder.mAliasList, builder.mFromID, builder.mToID, builder.mDetails); } - - public static Tait1200CallEvent getLJ1200Event( Tait1200GPSMessage message ) + + public static Tait1200CallEvent getLJ1200Event(Tait1200GPSMessage message) { CallEventType type = CallEventType.COMMAND; - - return new Tait1200CallEvent.Builder( type ) - .details( message.getMessage() ) - .build(); + + return new Tait1200CallEvent.Builder(type).details(message.toString()).build(); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java index 3fd46b95..db7574ef 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java @@ -1,21 +1,24 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.tait; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.IBinarySymbolProcessor; import io.github.dsheirer.bits.MessageFramer; import io.github.dsheirer.bits.SyncPattern; @@ -28,7 +31,6 @@ import io.github.dsheirer.module.decode.afsk.AbstractAFSKDecoder; */ public class Tait1200Decoder extends AbstractAFSKDecoder implements IBinarySymbolProcessor { - /* Message length ... */ private static final int MESSAGE_LENGTH = 440; private MessageFramer mMessageFramerGPS; @@ -36,27 +38,27 @@ public class Tait1200Decoder extends AbstractAFSKDecoder implements IBinarySymbo private Tait1200GPSMessageProcessor mMessageAProcessor; private Tait1200ANIMessageProcessor mMessageBProcessor; - protected Tait1200Decoder(AFSK1200Decoder decoder, AliasList aliasList) + protected Tait1200Decoder(AFSK1200Decoder decoder) { super(decoder); - init(aliasList); + init(); } - public Tait1200Decoder(AliasList aliasList) + public Tait1200Decoder() { super(AFSK1200Decoder.Output.NORMAL); - init(aliasList); + init(); } - private void init(AliasList aliasList) + private void init() { getDecoder().setSymbolProcessor(this); mMessageFramerGPS = new MessageFramer(SyncPattern.TAIT_CCDI_GPS_MESSAGE.getPattern(), MESSAGE_LENGTH); mMessageFramerANI = new MessageFramer(SyncPattern.TAIT_SELCAL_MESSAGE.getPattern(), MESSAGE_LENGTH); - mMessageAProcessor = new Tait1200GPSMessageProcessor(aliasList); - mMessageBProcessor = new Tait1200ANIMessageProcessor(aliasList); + mMessageAProcessor = new Tait1200GPSMessageProcessor(); + mMessageBProcessor = new Tait1200ANIMessageProcessor(); mMessageFramerGPS.addMessageListener(mMessageAProcessor); mMessageFramerANI.addMessageListener(mMessageBProcessor); @@ -81,10 +83,10 @@ public class Tait1200Decoder extends AbstractAFSKDecoder implements IBinarySymbo } @Override - public void receive(boolean symbol) + public void process(boolean symbol) { - mMessageFramerANI.receive(symbol); - mMessageFramerGPS.receive(symbol); + mMessageFramerANI.process(symbol); + mMessageFramerGPS.process(symbol); } public MessageFramer getANIMessageFramer() diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderInstrumented.java index 66f3100b..a9cfd979 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderInstrumented.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * 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.tait; import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented; @@ -29,7 +33,7 @@ public class Tait1200DecoderInstrumented extends Tait1200Decoder implements IIns public Tait1200DecoderInstrumented() { - super(new AFSK1200DecoderInstrumented(false), null); + super(new AFSK1200DecoderInstrumented(false)); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java index e82526bc..c2ce76a2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,48 +15,31 @@ * * 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.tait; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.id.AliasIDType; -import io.github.dsheirer.channel.metadata.AliasedStringAttributeMonitor; -import io.github.dsheirer.channel.metadata.Attribute; -import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.DecoderStateEvent.Event; import io.github.dsheirer.channel.state.State; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.MutableIdentifierCollection; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.util.StringUtils; +import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; +import io.github.dsheirer.module.decode.tait.identifier.TaitIdentifier; import org.jdesktop.swingx.mapviewer.GeoPosition; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.TreeSet; public class Tait1200DecoderState extends DecoderState { - private TreeSet mIdents = new TreeSet(); + private TreeSet mIdents = new TreeSet<>(); - private AliasedStringAttributeMonitor mFromAttribute; - private AliasedStringAttributeMonitor mToAttribute; - private String mMessage; - private String mMessageType; - - public Tait1200DecoderState(AliasList aliasList) + public Tait1200DecoderState() { - super(aliasList); - - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mToAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); } @Override @@ -68,7 +52,6 @@ public class Tait1200DecoderState extends DecoderState public void reset() { mIdents.clear(); - resetState(); } @@ -90,56 +73,51 @@ public class Tait1200DecoderState extends DecoderState } - private void resetState() - { - mFromAttribute.reset(); - mToAttribute.reset(); - mMessage = null; - mMessageType = null; - } - @Override - public void receive(Message message) + public void receive(IMessage message) { if(message instanceof Tait1200GPSMessage) { - Tait1200GPSMessage gps = (Tait1200GPSMessage) message; + Tait1200GPSMessage gps = (Tait1200GPSMessage)message; - mFromAttribute.process(gps.getFromID()); - mIdents.add(gps.getFromID()); - - mToAttribute.process(gps.getToID()); - mIdents.add(gps.getToID()); + mIdents.add(gps.getFromIdentifier()); + mIdents.add(gps.getToIdentifier()); GeoPosition position = gps.getGPSLocation(); if(position != null) { - setMessage(gps.getGPSLocation().toString().replace("[", "") - .replace("]", "")); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(message.getIdentifiers()); + + PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(gps.getTimestamp()) + .eventDescription("GPS") + .identifiers(ic) + .location(position) + .speed(gps.getSpeed()) + .build(); + + broadcast(event); } - setMessageType("GPS"); - - broadcast(new Tait1200CallEvent(CallEvent.CallEventType.GPS, getAliasList(), - gps.getFromID(), gps.getToID(), gps.getGPSLocation().toString())); - broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); } else if(message instanceof Tait1200ANIMessage) { - Tait1200ANIMessage ani = (Tait1200ANIMessage) message; - mFromAttribute.process(ani.getFromID()); - mIdents.add(ani.getFromID()); + Tait1200ANIMessage ani = (Tait1200ANIMessage)message; + mIdents.add(ani.getFromIdentifier()); + mIdents.add(ani.getToIdentifier()); - mToAttribute.process(ani.getToID()); - mIdents.add(ani.getToID()); + MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); + ic.remove(IdentifierClass.USER); + ic.update(message.getIdentifiers()); - setMessage(null); - setMessageType("ANI"); - - broadcast(new Tait1200CallEvent(CallEvent.CallEventType.ID_ANI, getAliasList(), - ani.getFromID(), ani.getToID(), "ANI")); + broadcast(DecodeEvent.builder(ani.getTimestamp()) + .eventDescription("ANI") + .identifiers(ic) + .details("Automatic Number Identification") + .build()); broadcast(new DecoderStateEvent(this, Event.DECODE, State.CALL)); } @@ -157,26 +135,9 @@ public class Tait1200DecoderState extends DecoderState { sb.append("Radio Identifiers:\n"); - List idents = new ArrayList(mIdents); - - Collections.sort(idents); - - for(String ident : idents) + for(TaitIdentifier taitIdentifier : mIdents) { - sb.append("\t"); - sb.append(ident); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(ident); - - if(alias != null) - { - sb.append("\t"); - sb.append(alias.getName()); - } - } - sb.append("\n"); + sb.append("\t").append(taitIdentifier).append("\n"); } } @@ -198,32 +159,4 @@ public class Tait1200DecoderState extends DecoderState break; } } - - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, getMessage())); - } - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType(String type) - { - if(!StringUtils.isEqual(mMessageType, type)) - { - mMessageType = type; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, getMessageType())); - } - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessage.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessage.java index 9d321fac..f2e413ac 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessage.java @@ -1,28 +1,30 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.tait; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.bits.BinaryMessage; import io.github.dsheirer.edac.CRC; -import io.github.dsheirer.map.Plottable; +import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.message.Message; +import io.github.dsheirer.module.decode.tait.identifier.TaitIdentifier; +import io.github.dsheirer.protocol.Protocol; import org.jdesktop.swingx.mapviewer.GeoPosition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,375 +39,285 @@ import java.util.TimeZone; public class Tait1200GPSMessage extends Message { - private final static Logger mLog = LoggerFactory.getLogger( Tait1200GPSMessage.class ); + private final static Logger mLog = LoggerFactory.getLogger(Tait1200GPSMessage.class); - public static int[] REVS_1 = { 0,1,2,3 }; - public static int[] SYNC = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }; - public static int[] SIZE = { 20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35 }; - public static int[] FROM_DIGIT_1 = { 36,37,38,39,40,41,42,43 }; - public static int[] FROM_DIGIT_2 = { 44,45,46,47,48,49,50,51 }; - public static int[] FROM_DIGIT_3 = { 52,53,54,55,56,57,58,59 }; - public static int[] FROM_DIGIT_4 = { 60,61,62,63,64,65,66,67 }; - public static int[] FROM_DIGIT_5 = { 68,69,70,71,72,73,74,75 }; - public static int[] FROM_DIGIT_6 = { 76,77,78,79,80,81,82,83 }; - public static int[] FROM_DIGIT_7 = { 84,85,86,87,88,89,90,91 }; - public static int[] FROM_DIGIT_8 = { 92,93,94,95,96,97,98,99 }; - public static int[] CHECKSUM_1 = { 100,101,102,103,104,105,106,107,108,109, - 110,111,112,113,114,115 }; + public static int[] REVS_1 = {0, 1, 2, 3}; + public static int[] SYNC = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + public static int[] SIZE = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + public static int[] FROM_DIGIT_1 = {36, 37, 38, 39, 40, 41, 42, 43}; + public static int[] FROM_DIGIT_2 = {44, 45, 46, 47, 48, 49, 50, 51}; + public static int[] FROM_DIGIT_3 = {52, 53, 54, 55, 56, 57, 58, 59}; + public static int[] FROM_DIGIT_4 = {60, 61, 62, 63, 64, 65, 66, 67}; + public static int[] FROM_DIGIT_5 = {68, 69, 70, 71, 72, 73, 74, 75}; + public static int[] FROM_DIGIT_6 = {76, 77, 78, 79, 80, 81, 82, 83}; + public static int[] FROM_DIGIT_7 = {84, 85, 86, 87, 88, 89, 90, 91}; + public static int[] FROM_DIGIT_8 = {92, 93, 94, 95, 96, 97, 98, 99}; + public static int[] CHECKSUM_1 = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115}; - public static int[] REVS_2 = { 116,117,118,119,120,121,122,123,124,125,126,127, - 128,129,130,131 }; - public static int[] SIZE_2 = { 188,189,190,191,192,193,194,195,196,197,198, - 199,200,201,202,203 }; - public static int[] TO_DIGIT_1 = { 204,205,206,207,208,209,210,211 }; - public static int[] TO_DIGIT_2 = { 212,213,214,215,216,217,218,219 }; - public static int[] TO_DIGIT_3 = { 220,221,222,223,224,225,226,227 }; - public static int[] TO_DIGIT_4 = { 228,229,230,231,232,233,234,235 }; - public static int[] TO_DIGIT_5 = { 236,237,238,239,240,241,242,243 }; - public static int[] TO_DIGIT_6 = { 244,245,246,247,248,249,250,251 }; - public static int[] TO_DIGIT_7 = { 252,253,254,255,256,257,258,259 }; - public static int[] TO_DIGIT_8 = { 260,261,262,263,264,265,266,267 }; - public static int[] UNKNOWN_1 = { 268,269,270,271,272,273,274,275 }; - public static int[] CHECKSUM_2 = { 276,277,278,279,280,281,282,283,284,285, - 286,287,288,289,290,291 }; - public static int DIVIDER_1 = 292; - public static int[] HOUR_TENS = { 293,294,295 }; - public static int[] HOUR_ONES = { 296,297,298,299 }; - public static int DIVIDER_2 = 300; - public static int[] MINUTES_TENS = { 301,302,303 }; - public static int[] MINUTES_ONES = { 304,305,306,307 }; - public static int DIVIDER_3 = 308; - public static int[] SECONDS_TENS = { 309,310,311 }; - public static int[] SECONDS_ONES = { 312,313,314,315 }; - public static int DIVIDER_4 = 316; - public static int[] LATITUDE_SIGN = { 317,318 }; - public static int DIVIDER_5 = 319; - public static int[] LATITUDE_DEGREES_TENS = { 320,321,322,323 }; - public static int[] LATITUDE_DEGREES_ONES = { 324,325,326,327 }; - public static int DIVIDER_6 = 328; - public static int[] LATITUDE_MINUTES_TENS = { 329,330,331 }; - public static int[] LATITUDE_MINUTES_ONES = { 332,333,334,335 }; - public static int[] LATITUDE_SECONDS_HUND = { 336,337,338,339 }; - public static int[] LATITUDE_SECONDS_TENS = { 340,341,342,344 }; - public static int[] LATITUDE_SECONDS_ONES = { 344,345,346,347 }; - public static int DIVIDER_7 = 348; - public static int[] LONGITUDE_SIGN = { 349,350 }; - public static int LONGITUDE_DEGREES_HUNDREDS = 351; - public static int[] LONGITUDE_DEGREES_TENS = { 352,353,354,355 }; - public static int[] LONGITUDE_DEGREES_ONES = { 356,357,358,359 }; - public static int DIVIDER_9 = 360; - public static int[] LONGITUDE_MINUTES_TENS = { 361,362,363 }; - public static int[] LONGITUDE_MINUTES_ONES = { 364,365,366,367 }; - public static int[] LONGITUDE_SECONDS_HUND = { 368,369,370,371 }; - public static int[] LONGITUDE_SECONDS_TENS = { 372,373,374,375 }; - public static int[] LONGITUDE_SECONDS_ONES = { 376,377,378,379 }; - public static int DIVIDER_10 = 380; - public static int[] DATE_DAY_TENS = { 381,382,383 }; - public static int[] DATE_DAY_ONES = { 384,385,386,387 }; + public static int[] REVS_2 = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131}; + public static int[] SIZE_2 = {188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203}; + public static int[] TO_DIGIT_1 = {204, 205, 206, 207, 208, 209, 210, 211}; + public static int[] TO_DIGIT_2 = {212, 213, 214, 215, 216, 217, 218, 219}; + public static int[] TO_DIGIT_3 = {220, 221, 222, 223, 224, 225, 226, 227}; + public static int[] TO_DIGIT_4 = {228, 229, 230, 231, 232, 233, 234, 235}; + public static int[] TO_DIGIT_5 = {236, 237, 238, 239, 240, 241, 242, 243}; + public static int[] TO_DIGIT_6 = {244, 245, 246, 247, 248, 249, 250, 251}; + public static int[] TO_DIGIT_7 = {252, 253, 254, 255, 256, 257, 258, 259}; + public static int[] TO_DIGIT_8 = {260, 261, 262, 263, 264, 265, 266, 267}; + public static int[] UNKNOWN_1 = {268, 269, 270, 271, 272, 273, 274, 275}; + public static int[] CHECKSUM_2 = {276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291}; + public static int DIVIDER_1 = 292; + public static int[] HOUR_TENS = {293, 294, 295}; + public static int[] HOUR_ONES = {296, 297, 298, 299}; + public static int DIVIDER_2 = 300; + public static int[] MINUTES_TENS = {301, 302, 303}; + public static int[] MINUTES_ONES = {304, 305, 306, 307}; + public static int DIVIDER_3 = 308; + public static int[] SECONDS_TENS = {309, 310, 311}; + public static int[] SECONDS_ONES = {312, 313, 314, 315}; + public static int DIVIDER_4 = 316; + public static int[] LATITUDE_SIGN = {317, 318}; + public static int DIVIDER_5 = 319; + public static int[] LATITUDE_DEGREES_TENS = {320, 321, 322, 323}; + public static int[] LATITUDE_DEGREES_ONES = {324, 325, 326, 327}; + public static int DIVIDER_6 = 328; + public static int[] LATITUDE_MINUTES_TENS = {329, 330, 331}; + public static int[] LATITUDE_MINUTES_ONES = {332, 333, 334, 335}; + public static int[] LATITUDE_SECONDS_HUND = {336, 337, 338, 339}; + public static int[] LATITUDE_SECONDS_TENS = {340, 341, 342, 344}; + public static int[] LATITUDE_SECONDS_ONES = {344, 345, 346, 347}; + public static int DIVIDER_7 = 348; + public static int[] LONGITUDE_SIGN = {349, 350}; + public static int LONGITUDE_DEGREES_HUNDREDS = 351; + public static int[] LONGITUDE_DEGREES_TENS = {352, 353, 354, 355}; + public static int[] LONGITUDE_DEGREES_ONES = {356, 357, 358, 359}; + public static int DIVIDER_9 = 360; + public static int[] LONGITUDE_MINUTES_TENS = {361, 362, 363}; + public static int[] LONGITUDE_MINUTES_ONES = {364, 365, 366, 367}; + public static int[] LONGITUDE_SECONDS_HUND = {368, 369, 370, 371}; + public static int[] LONGITUDE_SECONDS_TENS = {372, 373, 374, 375}; + public static int[] LONGITUDE_SECONDS_ONES = {376, 377, 378, 379}; + public static int DIVIDER_10 = 380; + public static int[] DATE_DAY_TENS = {381, 382, 383}; + public static int[] DATE_DAY_ONES = {384, 385, 386, 387}; - //TODO: not sure if this field is the month field or the speed field - public static int[] DATE_MONTH = { 388,389,390,391 }; - public static int[] SPEED_HUNDREDS = { 388,389,390,391 }; + //TODO: not sure if this field is the month field or the speed field + public static int[] DATE_MONTH = {388, 389, 390, 391}; + public static int[] SPEED_HUNDREDS = {388, 389, 390, 391}; - public static int[] SPEED_TENS = { 392,393,394,395 }; - public static int[] SPEED_ONES = { 396,397,398,399 }; - public static int[] SPEED_TENTHS = { 400,401,402,403 }; + public static int[] SPEED_TENS = {392, 393, 394, 395}; + public static int[] SPEED_ONES = {396, 397, 398, 399}; + public static int[] SPEED_TENTHS = {400, 401, 402, 403}; - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); + private static SimpleDateFormat mSDF = new SimpleDateFormat("yyyyMMdd HHmmss"); private BinaryMessage mMessage; - private AliasList mAliasList; private CRC mCRC; - - public Tait1200GPSMessage( BinaryMessage message, AliasList list ) + private TaitIdentifier mFromIdentifier; + private TaitIdentifier mToIdentifier; + private List mIdentifiers; + + public Tait1200GPSMessage(BinaryMessage message) { - mMessage = message; - mAliasList = list; + mMessage = message; } - + + public TaitIdentifier getFromIdentifier() + { + if(mFromIdentifier == null) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getCharacter(FROM_DIGIT_1)); + sb.append(getCharacter(FROM_DIGIT_2)); + sb.append(getCharacter(FROM_DIGIT_3)); + sb.append(getCharacter(FROM_DIGIT_4)); + sb.append(getCharacter(FROM_DIGIT_5)); + sb.append(getCharacter(FROM_DIGIT_6)); + sb.append(getCharacter(FROM_DIGIT_7)); + sb.append(getCharacter(FROM_DIGIT_8)); + + mFromIdentifier = TaitIdentifier.createFrom(sb.toString().trim()); + } + + return mFromIdentifier; + } + + public TaitIdentifier getToIdentifier() + { + if(mToIdentifier == null) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getCharacter(TO_DIGIT_1)); + sb.append(getCharacter(TO_DIGIT_2)); + sb.append(getCharacter(TO_DIGIT_3)); + sb.append(getCharacter(TO_DIGIT_4)); + sb.append(getCharacter(TO_DIGIT_5)); + sb.append(getCharacter(TO_DIGIT_6)); + sb.append(getCharacter(TO_DIGIT_7)); + sb.append(getCharacter(TO_DIGIT_8)); + + mToIdentifier = TaitIdentifier.createTo(sb.toString().trim()); + } + + return mToIdentifier; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getFromIdentifier()); + mIdentifiers.add(getToIdentifier()); + } + + return mIdentifiers; + } + public boolean isValid() { - //TODO: Override until we figure out the CRC - return true; + //TODO: Override until we figure out the CRC + return true; } - + public int getMessage1Size() { - return mMessage.getInt( SIZE ); + return mMessage.getInt(SIZE); } - + public int getMessage2Size() { - return mMessage.getInt( SIZE_2 ); + return mMessage.getInt(SIZE_2); } @Override public String toString() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( "GPS FROM:" ); - sb.append( getFromID() ); - sb.append( " TO:" ); - sb.append( getToID() ); - sb.append( " LOCATION:" ); - - GeoPosition location = getGPSLocation(); - - sb.append( location.getLatitude() ); - sb.append( " " ); - sb.append( location.getLongitude() ); - - sb.append( " SPEED:" ); - sb.append( getSpeed() ); - sb.append( "KPH" ); - - sb.append( " GPS TIME:" ); - sb.append( mSDF.format( new Date( getGPSTime() ) ) ); - - sb.append( " " ); - sb.append( mMessage.toString() ); + sb.append("GPS FROM:").append(getFromIdentifier()); + sb.append(" TO:").append(getToIdentifier()); + sb.append(" LOCATION:"); - return sb.toString(); + GeoPosition location = getGPSLocation(); + + sb.append(location.getLatitude()); + sb.append(" "); + sb.append(location.getLongitude()); + + sb.append(" SPEED:").append(getSpeed()).append("KPH"); + sb.append(" GPS TIME:"); + sb.append(mSDF.format(new Date(getGPSTime()))); + + sb.append(" "); + sb.append(mMessage.toString()); + + return sb.toString(); } - + public GeoPosition getGPSLocation() { - double latitude = mMessage.getInt( LATITUDE_DEGREES_TENS ) * 10.0d; - latitude += mMessage.getInt( LATITUDE_DEGREES_ONES ); - latitude += (double)mMessage.getInt( LATITUDE_MINUTES_TENS ) / 6.0d; - latitude += (double)mMessage.getInt( LATITUDE_MINUTES_ONES ) / 60.0d; - latitude += (double)mMessage.getInt( LATITUDE_SECONDS_HUND ) / 600.0d; - latitude += (double)mMessage.getInt( LATITUDE_SECONDS_TENS ) / 6000.0d; - latitude += (double)mMessage.getInt( LATITUDE_SECONDS_ONES ) / 60000.0d; + double latitude = mMessage.getInt(LATITUDE_DEGREES_TENS) * 10.0d; + latitude += mMessage.getInt(LATITUDE_DEGREES_ONES); + latitude += (double)mMessage.getInt(LATITUDE_MINUTES_TENS) / 6.0d; + latitude += (double)mMessage.getInt(LATITUDE_MINUTES_ONES) / 60.0d; + latitude += (double)mMessage.getInt(LATITUDE_SECONDS_HUND) / 600.0d; + latitude += (double)mMessage.getInt(LATITUDE_SECONDS_TENS) / 6000.0d; + latitude += (double)mMessage.getInt(LATITUDE_SECONDS_ONES) / 60000.0d; - if( mMessage.getInt( LATITUDE_SIGN ) == 0 ) - { - latitude *= -1; - } - - double longitude = mMessage.get( LONGITUDE_DEGREES_HUNDREDS ) ? 100.0d : 0.0d; + if(mMessage.getInt(LATITUDE_SIGN) == 0) + { + latitude *= -1; + } - longitude += mMessage.getInt( LONGITUDE_DEGREES_TENS ) * 10.0d; - longitude += mMessage.getInt( LONGITUDE_DEGREES_ONES ); - longitude += (double)mMessage.getInt( LONGITUDE_MINUTES_TENS ) / 6.0d; - longitude += (double)mMessage.getInt( LONGITUDE_MINUTES_ONES ) / 60.0d; - longitude += (double)mMessage.getInt( LONGITUDE_SECONDS_HUND ) / 600.0d; - longitude += (double)mMessage.getInt( LONGITUDE_SECONDS_TENS ) / 6000.0d; - longitude += (double)mMessage.getInt( LONGITUDE_SECONDS_ONES ) / 60000.0d; - - if( mMessage.getInt( LONGITUDE_SIGN ) == 0 ) - { - longitude = -1; - } - - return new GeoPosition( latitude, longitude ); + double longitude = mMessage.get(LONGITUDE_DEGREES_HUNDREDS) ? 100.0d : 0.0d; + + longitude += mMessage.getInt(LONGITUDE_DEGREES_TENS) * 10.0d; + longitude += mMessage.getInt(LONGITUDE_DEGREES_ONES); + longitude += (double)mMessage.getInt(LONGITUDE_MINUTES_TENS) / 6.0d; + longitude += (double)mMessage.getInt(LONGITUDE_MINUTES_ONES) / 60.0d; + longitude += (double)mMessage.getInt(LONGITUDE_SECONDS_HUND) / 600.0d; + longitude += (double)mMessage.getInt(LONGITUDE_SECONDS_TENS) / 6000.0d; + longitude += (double)mMessage.getInt(LONGITUDE_SECONDS_ONES) / 60000.0d; + + if(mMessage.getInt(LONGITUDE_SIGN) == 0) + { + longitude = -1; + } + + return new GeoPosition(latitude, longitude); } - + public long getGPSTime() { - Calendar cal = new GregorianCalendar( TimeZone.getTimeZone( "UTC" ) ); - - cal.clear(); - - /* Use current time to get current year */ - cal.setTimeInMillis( System.currentTimeMillis() ); + Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - //TODO: this field is either the month field, or the speed hundredths field - cal.set( Calendar.MONTH, mMessage.getInt( DATE_MONTH ) ); - - int day = mMessage.getInt( DATE_DAY_TENS ) * 10 + - mMessage.getInt( DATE_DAY_ONES ); - cal.set( Calendar.DAY_OF_MONTH, day ); - - cal.set( Calendar.HOUR_OF_DAY, ( mMessage.getInt( HOUR_TENS ) * 10 ) + - mMessage.getInt( HOUR_ONES ) ); - cal.set( Calendar.MINUTE, ( mMessage.getInt( MINUTES_TENS ) * 10 ) + - mMessage.getInt( MINUTES_ONES ) ); - cal.set( Calendar.SECOND, ( mMessage.getInt( SECONDS_TENS ) * 10 ) + - mMessage.getInt( SECONDS_ONES ) ); - cal.set( Calendar.MILLISECOND, 0 ); - - return cal.getTimeInMillis(); - } - - public String getSpeed() - { - StringBuilder sb = new StringBuilder(); + cal.clear(); - sb.append( getDigit( SPEED_HUNDREDS ) ); - sb.append( getDigit( SPEED_TENS ) ); - sb.append( getDigit( SPEED_ONES ) ); - sb.append( "." ); - sb.append( getDigit( SPEED_TENTHS ) ); - - return sb.toString(); - } - - private String getDigit( int[] field ) - { - int value = mMessage.getInt( field ); - return ( 0 <= value && value <= 9 ) ? String.valueOf( value ) : "?"; - } - - @Override - public String getFromID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getCharacter( FROM_DIGIT_1 ) ); - sb.append( getCharacter( FROM_DIGIT_2 ) ); - sb.append( getCharacter( FROM_DIGIT_3 ) ); - sb.append( getCharacter( FROM_DIGIT_4 ) ); - sb.append( getCharacter( FROM_DIGIT_5 ) ); - sb.append( getCharacter( FROM_DIGIT_6 ) ); - sb.append( getCharacter( FROM_DIGIT_7 ) ); - sb.append( getCharacter( FROM_DIGIT_8 ) ); - - return sb.toString(); - } - - @Override - public Alias getFromIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getFromID() ); - } - - return null; - } - - @Override - public String getToID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getCharacter( TO_DIGIT_1 ) ); - sb.append( getCharacter( TO_DIGIT_2 ) ); - sb.append( getCharacter( TO_DIGIT_3 ) ); - sb.append( getCharacter( TO_DIGIT_4 ) ); - sb.append( getCharacter( TO_DIGIT_5 ) ); - sb.append( getCharacter( TO_DIGIT_6 ) ); - sb.append( getCharacter( TO_DIGIT_7 ) ); - sb.append( getCharacter( TO_DIGIT_8 ) ); - - return sb.toString(); - } - - @Override - public Alias getToIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getToID() ); - } - - return null; - } - - public char getCharacter( int[] bits ) - { - int value = mMessage.getInt( bits ); - - return (char)value; + /* Use current time to get current year */ + cal.setTimeInMillis(System.currentTimeMillis()); + + //TODO: this field is either the month field, or the speed hundredths field + cal.set(Calendar.MONTH, mMessage.getInt(DATE_MONTH)); + + int day = mMessage.getInt(DATE_DAY_TENS) * 10 + + mMessage.getInt(DATE_DAY_ONES); + cal.set(Calendar.DAY_OF_MONTH, day); + + cal.set(Calendar.HOUR_OF_DAY, (mMessage.getInt(HOUR_TENS) * 10) + + mMessage.getInt(HOUR_ONES)); + cal.set(Calendar.MINUTE, (mMessage.getInt(MINUTES_TENS) * 10) + + mMessage.getInt(MINUTES_ONES)); + cal.set(Calendar.SECOND, (mMessage.getInt(SECONDS_TENS) * 10) + + mMessage.getInt(SECONDS_ONES)); + cal.set(Calendar.MILLISECOND, 0); + + return cal.getTimeInMillis(); } - /** - * Pads spaces onto the end of the value to make it 'places' long - */ - public String pad( String value, int places, String padCharacter ) + public double getSpeed() { - StringBuilder sb = new StringBuilder(); - - sb.append( value ); - - while( sb.length() < places ) - { - sb.append( padCharacter ); - } - - return sb.toString(); - } - - /** - * Pads an integer value with additional zeroes to make it decimalPlaces long - */ - public String format( int number, int decimalPlaces ) - { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); + sb.append(getDigit(SPEED_HUNDREDS)); + sb.append(getDigit(SPEED_TENS)); + sb.append(getDigit(SPEED_ONES)); + sb.append("."); + sb.append(getDigit(SPEED_TENTHS)); - int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() ); + try + { + return Double.parseDouble(sb.toString()); + } + catch(Exception e) + { + //Do nothing, we couldn't parse the value + } - for( int x = 0; x < paddingRequired; x++) - { - sb.append( "0" ); - } - - sb.append( number ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } - - @Override - public String getProtocol() - { - return "Tait-1200"; + return 0; } - @Override - public String getEventType() + private String getDigit(int[] field) { - return "GPS"; + int value = mMessage.getInt(field); + return (0 <= value && value <= 9) ? String.valueOf(value) : "?"; } - @Override - public String getMessage() + public char getCharacter(int[] bits) { - return toString(); - } - - @Override - public String getErrorStatus() - { - return "UNKNOWN"; -// return mCRC.getDisplayText(); + int value = mMessage.getInt(bits); + return (char)value; } - @Override - public Plottable getPlottable() + @Override + public Protocol getProtocol() { - return new Plottable( getGPSTime(), getGPSLocation(), getFromID(), - getFromIDAlias() ); + return Protocol.TAIT1200; } - - /** - * Provides a listing of aliases contained in the message. - */ - public List getAliases() - { - List aliases = new ArrayList(); - - Alias from = getFromIDAlias(); - - if( from != null ) - { - aliases.add( from ); - } - - Alias to = getToIDAlias(); - - if( to != null ) - { - aliases.add( to ); - } - - return aliases; - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessageProcessor.java index c9bf06ef..fbb25f2a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200GPSMessageProcessor.java @@ -1,65 +1,61 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.tait; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.bits.BinaryMessage; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Tait1200GPSMessageProcessor implements Listener +public class Tait1200GPSMessageProcessor implements Listener { - private final static Logger mLog = - LoggerFactory.getLogger( Tait1200GPSMessageProcessor.class ); + private final static Logger mLog = LoggerFactory.getLogger(Tait1200GPSMessageProcessor.class); - private Listener mMessageListener; - - private AliasList mAliasList; - - public Tait1200GPSMessageProcessor( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mMessageListener = null; - mAliasList = null; - } - - @Override - public void receive( BinaryMessage buffer ) + private Listener mMessageListener; + + public Tait1200GPSMessageProcessor() { - if( mMessageListener != null ) - { - mMessageListener.receive( new Tait1200GPSMessage( buffer, mAliasList ) ); - } } - - public void setMessageListener( Listener listener ) + + public void dispose() { - mMessageListener = listener; + mMessageListener = null; + } + + @Override + public void receive(CorrectedBinaryMessage buffer) + { + if(mMessageListener != null) + { + mMessageListener.receive(new Tait1200GPSMessage(buffer)); + } + } + + public void setMessageListener(Listener listener) + { + mMessageListener = listener; } public void removeMessageListener() { - mMessageListener = null; + mMessageListener = null; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/identifier/TaitIdentifier.java b/src/main/java/io/github/dsheirer/module/decode/tait/identifier/TaitIdentifier.java new file mode 100644 index 00000000..32b5148a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/tait/identifier/TaitIdentifier.java @@ -0,0 +1,63 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.module.decode.tait.identifier; + +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.string.StringIdentifier; +import io.github.dsheirer.protocol.Protocol; + +/** + * Tait-1200 String (ASCII) Identifier + */ +public class TaitIdentifier extends StringIdentifier +{ + /** + * Constructs an identifier with the specified role + */ + public TaitIdentifier(String value, Role role) + { + super(value, IdentifierClass.USER, Form.UNIT_IDENTIFIER, role); + } + + @Override + public Protocol getProtocol() + { + return Protocol.TAIT1200; + } + + /*** + * Creates an identifier with the FROM role + */ + public static TaitIdentifier createFrom(String value) + { + return new TaitIdentifier(value, Role.FROM); + } + + /*** + * Creates an identifier with the TO role + */ + public static TaitIdentifier createTo(String value) + { + return new TaitIdentifier(value, Role.TO); + } +} diff --git a/src/main/java/io/github/dsheirer/module/log/CallEventLogger.java b/src/main/java/io/github/dsheirer/module/log/CallEventLogger.java deleted file mode 100644 index 2f9784f2..00000000 --- a/src/main/java/io/github/dsheirer/module/log/CallEventLogger.java +++ /dev/null @@ -1,62 +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 - ******************************************************************************/ -package io.github.dsheirer.module.log; - -import io.github.dsheirer.module.decode.event.CallEvent; -import io.github.dsheirer.module.decode.event.ICallEventListener; -import io.github.dsheirer.sample.Listener; - -import java.nio.file.Path; - -public class CallEventLogger extends EventLogger implements ICallEventListener, - Listener -{ - public CallEventLogger( Path logDirectory, String fileNameSuffix ) - { - super( logDirectory, fileNameSuffix ); - } - - @Override - public void receive( CallEvent callEvent ) - { - write( callEvent.toCSV() ); - } - - @Override - public String getHeader() - { - return CallEvent.getCSVHeader(); - } - - @Override - public Listener getCallEventListener() - { - return this; - } - - @Override - public void dispose() - { - super.stop(); - } - - @Override - public void reset() - { - } -} diff --git a/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java b/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java new file mode 100644 index 00000000..68d75393 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java @@ -0,0 +1,136 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.module.log; + +import io.github.dsheirer.channel.IChannelDescriptor; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier; +import io.github.dsheirer.module.decode.event.IDecodeEvent; +import io.github.dsheirer.module.decode.event.IDecodeEventListener; +import io.github.dsheirer.preference.TimestampFormat; +import io.github.dsheirer.sample.Listener; + +import java.nio.file.Path; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class DecodeEventLogger extends EventLogger implements IDecodeEventListener, Listener +{ + private SimpleDateFormat mTimestampFormat = TimestampFormat.TIMESTAMP_COLONS.getFormatter(); + private DecimalFormat mFrequencyFormat = new DecimalFormat("0.000000"); + + public DecodeEventLogger(Path logDirectory, String fileNameSuffix, long frequency) + { + super(logDirectory, fileNameSuffix, frequency); + } + + @Override + public void receive(IDecodeEvent decodeEvent) + { + write(toCSV(decodeEvent)); + } + + @Override + public String getHeader() + { + return getCSVHeader(); + } + + @Override + public Listener getDecodeEventListener() + { + return this; + } + + @Override + public void dispose() + { + super.stop(); + } + + @Override + public void reset() + { + } + + public static String getCSVHeader() + { + return "TIMESTAMP,DURATION_MS,PROTOCOL,EVENT,FROM,TO,CHANNEL_NUMBER,FREQUENCY,DETAILS"; + } + + private String toCSV(IDecodeEvent event) + { + StringBuilder sb = new StringBuilder(); + + sb.append("\"").append(mTimestampFormat.format(new Date(event.getTimeStart()))).append("\""); + sb.append(",\"").append(event.getDuration() > 0 ? event.getDuration() : "").append("\""); + sb.append(",\"").append(event.getProtocol()).append("\""); + + String description = event.getEventDescription(); + + sb.append(",\"").append(description != null ? description : "").append("\""); + + List fromIdentifiers = event.getIdentifierCollection().getIdentifiers(Role.FROM); + if(fromIdentifiers != null && !fromIdentifiers.isEmpty()) + { + sb.append(",\"").append(fromIdentifiers.get(0)).append("\""); + } + else + { + sb.append(",\"\""); + } + + List toIdentifiers = event.getIdentifierCollection().getIdentifiers(Role.TO); + if(toIdentifiers != null && !toIdentifiers.isEmpty()) + { + sb.append(",\"").append(toIdentifiers.get(0)).append("\""); + } + else + { + sb.append(",\"\""); + } + + IChannelDescriptor descriptor = event.getChannelDescriptor(); + + sb.append(",\"").append(descriptor != null ? descriptor : "").append("\""); + + Identifier frequency = event.getIdentifierCollection().getIdentifier(IdentifierClass.CONFIGURATION, Form.CHANNEL_FREQUENCY, Role.ANY); + + if(frequency instanceof FrequencyConfigurationIdentifier) + { + sb.append(",\"").append(mFrequencyFormat.format(((FrequencyConfigurationIdentifier)frequency).getValue() / 1e6d)).append("\""); + } + else + { + sb.append(",\"\""); + } + + String details = event.getDetails(); + + sb.append(",\"").append(details != null ? details : "").append("\""); + + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogConfigurationEditor.java b/src/main/java/io/github/dsheirer/module/log/EventLogConfigurationEditor.java index 9e9dc0d3..9a8302bc 100644 --- a/src/main/java/io/github/dsheirer/module/log/EventLogConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/module/log/EventLogConfigurationEditor.java @@ -1,20 +1,22 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.module.log; import io.github.dsheirer.controller.channel.Channel; @@ -22,7 +24,7 @@ import io.github.dsheirer.gui.editor.Editor; import io.github.dsheirer.module.log.config.EventLogConfiguration; import net.miginfocom.swing.MigLayout; -import javax.swing.*; +import javax.swing.JCheckBox; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; @@ -30,121 +32,144 @@ import java.util.List; public class EventLogConfigurationEditor extends Editor { private static final long serialVersionUID = 1L; - - private JCheckBox mBinaryLogger; - private JCheckBox mDecodedLogger; + private JCheckBox mCallEventLogger; + private JCheckBox mDecodedMessageLogger; + private JCheckBox mTrafficCallEventLogger; + private JCheckBox mTrafficDecodedMessageLogger; public EventLogConfigurationEditor() - { - init(); - } - + { + init(); + } + private void init() { - setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); + setLayout(new MigLayout("fill,wrap 2", "", "[][][grow]")); - mBinaryLogger = new JCheckBox( "Binary Messages" ); - mBinaryLogger.setEnabled( false ); - mBinaryLogger.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - add( mBinaryLogger ); - - mDecodedLogger = new JCheckBox( "Decoded Messages" ); - mDecodedLogger.setEnabled( false ); - mDecodedLogger.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - add( mDecodedLogger ); - - mCallEventLogger = new JCheckBox( "Call Events" ); - mCallEventLogger.setEnabled( false ); - mCallEventLogger.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - add( mCallEventLogger ); + mCallEventLogger = new JCheckBox("Call Events"); + mCallEventLogger.setEnabled(false); + mCallEventLogger.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + setModified(true); + } + }); + add(mCallEventLogger); + + mDecodedMessageLogger = new JCheckBox("Decoded Messages"); + mDecodedMessageLogger.setEnabled(false); + mDecodedMessageLogger.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + setModified(true); + } + }); + add(mDecodedMessageLogger); + + mTrafficCallEventLogger = new JCheckBox("Traffic Channel Call Events"); + mTrafficCallEventLogger.setEnabled(false); + mTrafficCallEventLogger.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + setModified(true); + } + }); + add(mTrafficCallEventLogger); + + mTrafficDecodedMessageLogger = new JCheckBox("Traffic Channel Decoded Messages"); + mTrafficDecodedMessageLogger.setEnabled(false); + mTrafficDecodedMessageLogger.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + setModified(true); + } + }); + add(mTrafficDecodedMessageLogger); } /** * Saves the current configuration as the stored configuration */ - @Override + @Override public void save() { - if( hasItem() ) - { - EventLogConfiguration config = getItem().getEventLogConfiguration(); + if(hasItem()) + { + EventLogConfiguration config = getItem().getEventLogConfiguration(); - config.clear(); - - if( mBinaryLogger.isSelected() ) - { - config.addLogger( EventLogType.BINARY_MESSAGE ); - } - if( mDecodedLogger.isSelected() ) - { - config.addLogger( EventLogType.DECODED_MESSAGE ); - } - if( mCallEventLogger.isSelected() ) - { - config.addLogger( EventLogType.CALL_EVENT ); - } - } - - setModified( false ); + config.clear(); + + if(mCallEventLogger.isSelected()) + { + config.addLogger(EventLogType.CALL_EVENT); + } + if(mDecodedMessageLogger.isSelected()) + { + config.addLogger(EventLogType.DECODED_MESSAGE); + } + if(mTrafficCallEventLogger.isSelected()) + { + config.addLogger(EventLogType.TRAFFIC_CALL_EVENT); + } + if(mTrafficDecodedMessageLogger.isSelected()) + { + config.addLogger(EventLogType.TRAFFIC_DECODED_MESSAGE); + } + } + + setModified(false); } - @Override - public void setItem( Channel channel ) - { - super.setItem( channel ); + @Override + public void setItem(Channel channel) + { + super.setItem(channel); - if( hasItem() ) - { - setControlsEnabled( true ); + if(hasItem()) + { + setControlsEnabled(true); - List loggers = getItem().getEventLogConfiguration().getLoggers(); - mBinaryLogger.setSelected( loggers.contains( EventLogType.BINARY_MESSAGE ) ); - mDecodedLogger.setSelected( loggers.contains( EventLogType.DECODED_MESSAGE ) ); - mCallEventLogger.setSelected( loggers.contains( EventLogType.CALL_EVENT ) ); - } - else - { - setControlsEnabled( false ); - } - } - - private void setControlsEnabled( boolean enabled ) - { - if( mBinaryLogger.isEnabled() != enabled ) - { - mBinaryLogger.setEnabled( enabled ); - } - - if( mDecodedLogger.isEnabled() != enabled ) - { - mDecodedLogger.setEnabled( enabled ); - } - - if( mCallEventLogger.isEnabled() != enabled ) - { - mCallEventLogger.setEnabled( enabled ); - } - } + List loggers = getItem().getEventLogConfiguration().getLoggers(); + mDecodedMessageLogger.setSelected(loggers.contains(EventLogType.DECODED_MESSAGE)); + mCallEventLogger.setSelected(loggers.contains(EventLogType.CALL_EVENT)); + mTrafficCallEventLogger.setSelected(loggers.contains(EventLogType.TRAFFIC_CALL_EVENT)); + mTrafficDecodedMessageLogger.setSelected(loggers.contains(EventLogType.TRAFFIC_DECODED_MESSAGE)); + } + else + { + setControlsEnabled(false); + } + } + + private void setControlsEnabled(boolean enabled) + { + if(mDecodedMessageLogger.isEnabled() != enabled) + { + mDecodedMessageLogger.setEnabled(enabled); + } + + if(mCallEventLogger.isEnabled() != enabled) + { + mCallEventLogger.setEnabled(enabled); + } + + if(mTrafficDecodedMessageLogger.isEnabled() != enabled) + { + mTrafficDecodedMessageLogger.setEnabled(enabled); + } + + if(mTrafficCallEventLogger.isEnabled() != enabled) + { + mTrafficCallEventLogger.setEnabled(enabled); + } + } } diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogManager.java b/src/main/java/io/github/dsheirer/module/log/EventLogManager.java index 74d29363..b552d4eb 100644 --- a/src/main/java/io/github/dsheirer/module/log/EventLogManager.java +++ b/src/main/java/io/github/dsheirer/module/log/EventLogManager.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,13 +15,16 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.module.log; +import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.module.Module; import io.github.dsheirer.module.log.config.EventLogConfiguration; import io.github.dsheirer.properties.SystemProperties; +import io.github.dsheirer.source.config.SourceConfigTuner; +import io.github.dsheirer.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,46 +34,73 @@ import java.util.List; public class EventLogManager { - private final static Logger mLog = LoggerFactory.getLogger(EventLogManager.class); + private final static Logger mLog = LoggerFactory.getLogger(EventLogManager.class); - private Path mDirectory; - - public EventLogManager() - { - mDirectory = SystemProperties.getInstance() - .getApplicationFolder( "event_logs" ); - } + private Path mDirectory; - public List getLoggers(EventLogConfiguration config, String prefix ) - { - List loggers = new ArrayList(); + public EventLogManager() + { + mDirectory = SystemProperties.getInstance() + .getApplicationFolder("event_logs"); + } - for( EventLogType type: config.getLoggers() ) - { - loggers.add( getLogger( type, prefix ) ); - } + public List getLoggers(Channel channel) + { + EventLogConfiguration config = channel.getEventLogConfiguration(); + String prefix = StringUtils.replaceIllegalCharacters(channel.getName()); + long frequency = 0; - return loggers; - } - - public EventLogger getLogger( EventLogType eventLogType, String prefix ) - { - StringBuilder sb = new StringBuilder(); + if(channel.getSourceConfiguration() instanceof SourceConfigTuner) + { + frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency(); + } - sb.append( prefix ); - sb.append( eventLogType.getFileSuffix() ); - sb.append( ".log" ); + List loggers = new ArrayList(); - switch( eventLogType ) - { - case BINARY_MESSAGE: - return new MessageEventLogger( mDirectory, sb.toString(), MessageEventLogger.Type.BINARY ); - case DECODED_MESSAGE: - return new MessageEventLogger( mDirectory, sb.toString(), MessageEventLogger.Type.DECODED ); - case CALL_EVENT: - return new CallEventLogger( mDirectory, sb.toString() ); - default: - return null; - } - } + for(EventLogType type : config.getLoggers()) + { + switch(type) + { + case CALL_EVENT: + case DECODED_MESSAGE: + if(channel.getChannelType() == Channel.ChannelType.STANDARD) + { + loggers.add(getLogger(type, prefix, frequency)); + } + break; + case TRAFFIC_CALL_EVENT: + case TRAFFIC_DECODED_MESSAGE: + if(channel.getChannelType() == Channel.ChannelType.TRAFFIC) + { + loggers.add(getLogger(type, prefix, frequency)); + } + break; + } + } + + return loggers; + } + + public EventLogger getLogger(EventLogType eventLogType, String prefix, long frequency) + { + StringBuilder sb = new StringBuilder(); + + sb.append(prefix); + sb.append(eventLogType.getFileSuffix()); + sb.append(".log"); + + switch(eventLogType) + { + case CALL_EVENT: + return new DecodeEventLogger(mDirectory, sb.toString(), frequency); + case DECODED_MESSAGE: + return new MessageEventLogger(mDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency); + case TRAFFIC_CALL_EVENT: + return new DecodeEventLogger(mDirectory, sb.toString(), frequency); + case TRAFFIC_DECODED_MESSAGE: + return new MessageEventLogger(mDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency); + default: + return null; + } + } } diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogType.java b/src/main/java/io/github/dsheirer/module/log/EventLogType.java index 534e4677..51f2d3c6 100644 --- a/src/main/java/io/github/dsheirer/module/log/EventLogType.java +++ b/src/main/java/io/github/dsheirer/module/log/EventLogType.java @@ -20,10 +20,16 @@ package io.github.dsheirer.module.log; public enum EventLogType { + /** + * Legacy logging type - do not use - superceded by bitstream recorder + */ BINARY_MESSAGE( "Binary Messages", "_binary_messages" ), + DECODED_MESSAGE( "Decoded Messages", "_decoded_messages" ), - CALL_EVENT( "Call Events", "_call_events" ); - + TRAFFIC_DECODED_MESSAGE( "Traffic Channel Decoded Messages", "_decoded_messages" ), + CALL_EVENT( "Call Events", "_call_events" ), + TRAFFIC_CALL_EVENT( "Traffic Channel Call Events", "_call_events" ); + private String mDisplayString; private String mFileSuffix; diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogger.java b/src/main/java/io/github/dsheirer/module/log/EventLogger.java index 242a9ba9..c391c60b 100644 --- a/src/main/java/io/github/dsheirer/module/log/EventLogger.java +++ b/src/main/java/io/github/dsheirer/module/log/EventLogger.java @@ -26,7 +26,6 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Path; @@ -38,12 +37,14 @@ public abstract class EventLogger extends Module private Path mLogDirectory; private String mFileNameSuffix; private String mLogFileName; + private long mFrequency; protected Writer mLogFile; - public EventLogger(Path logDirectory, String fileNameSuffix) + public EventLogger(Path logDirectory, String fileNameSuffix, long frequency) { mLogDirectory = logDirectory; mFileNameSuffix = fileNameSuffix; + mFrequency = frequency; } public String toString() @@ -70,8 +71,10 @@ public abstract class EventLogger extends Module StringBuilder sb = new StringBuilder(); sb.append(mLogDirectory); sb.append(File.separator); - sb.append(TimeStamp.getTimeStamp("_")); + sb.append(TimeStamp.getLongTimeStamp("_")); sb.append("_"); + sb.append(mFrequency); + sb.append("_Hz_"); sb.append(mFileNameSuffix); mLogFileName = sb.toString(); @@ -110,10 +113,13 @@ public abstract class EventLogger extends Module { try { - mLogFile.write(eventLogEntry + "\n"); - mLogFile.flush(); + if(mLogFile != null) + { + mLogFile.write((eventLogEntry != null ? eventLogEntry : "") + "\n"); + mLogFile.flush(); + } } - catch(IOException e) + catch(Exception e) { mLog.error("Error writing entry to event log file", e); } diff --git a/src/main/java/io/github/dsheirer/module/log/MessageEventLogger.java b/src/main/java/io/github/dsheirer/module/log/MessageEventLogger.java index 0d1f6b14..9ba3737d 100644 --- a/src/main/java/io/github/dsheirer/module/log/MessageEventLogger.java +++ b/src/main/java/io/github/dsheirer/module/log/MessageEventLogger.java @@ -1,88 +1,77 @@ /******************************************************************************* * 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 ******************************************************************************/ package io.github.dsheirer.module.log; +import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessageListener; -import io.github.dsheirer.message.Message; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.TimeStamp; import java.nio.file.Path; -public class MessageEventLogger extends EventLogger implements - IMessageListener, Listener +public class MessageEventLogger extends EventLogger implements IMessageListener, Listener { - public enum Type - { - BINARY, DECODED - }; + public enum Type + { + BINARY, DECODED + } - private Type mType; + private Type mType; - public MessageEventLogger( Path logDirectory, - String fileNameSuffix, - Type type ) - { - super( logDirectory, fileNameSuffix ); + public MessageEventLogger(Path logDirectory, String fileNameSuffix, Type type, long frequency) + { + super(logDirectory, fileNameSuffix, frequency); + mType = type; + } - mType = type; - } + @Override + public Listener getMessageListener() + { + return this; + } - @Override - public Listener getMessageListener() - { - return this; - } + @Override + public void dispose() + { + super.stop(); + } - @Override - public void dispose() - { - super.stop(); - } + @Override + public void reset() + { + } - @Override - public void reset() - { - } + @Override + public void receive(IMessage message) + { + StringBuilder sb = new StringBuilder(); + sb.append(TimeStamp.getTimeStamp(message.getTimestamp(), " ")); + sb.append(","); + sb.append((message.isValid() ? "PASSED" : "FAILED")); + sb.append(","); + sb.append(message.toString()); - @Override - public void receive( Message message ) - { - StringBuilder sb = new StringBuilder(); - sb.append( TimeStamp.getTimeStamp( " " ) ); - sb.append( "," ); - sb.append( ( message.isValid() ? "PASSED" : "FAILED" ) ); - sb.append( "," ); + write(sb.toString()); + } - if ( mType == Type.BINARY ) - { - sb.append( message.getBinaryMessage() ); - } else - { - sb.append( message.getMessage() ); - } - - write( sb.toString() ); - } - - @Override - public String getHeader() - { - return mType.toString() + " Message Logger\n"; - } + @Override + public String getHeader() + { + return mType.toString() + " Message Logger\n"; + } } diff --git a/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java b/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java index 472ce4f1..3ee2267a 100644 --- a/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java +++ b/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java @@ -1,18 +1,21 @@ /* - * ********************************************************************************************************************* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 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. + * 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 - * ********************************************************************************************************************* + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** */ package io.github.dsheirer.playlist; @@ -26,11 +29,11 @@ import io.github.dsheirer.audio.broadcast.BroadcastEvent; import io.github.dsheirer.audio.broadcast.BroadcastModel; import io.github.dsheirer.controller.channel.Channel.ChannelType; import io.github.dsheirer.controller.channel.ChannelEvent; -import io.github.dsheirer.controller.channel.ChannelEventListener; import io.github.dsheirer.controller.channel.ChannelModel; import io.github.dsheirer.controller.channel.map.ChannelMapEvent; import io.github.dsheirer.controller.channel.map.ChannelMapModel; -import io.github.dsheirer.properties.SystemProperties; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.playlist.FilePreferences; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; @@ -40,25 +43,21 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -public class PlaylistManager implements ChannelEventListener +public class PlaylistManager implements Listener { private final static Logger mLog = LoggerFactory.getLogger(PlaylistManager.class); + public static final int PLAYLIST_CURRENT_VERSION = 2; + private AliasModel mAliasModel; private BroadcastModel mBroadcastModel; private ChannelModel mChannelModel; private ChannelMapModel mChannelMapModel; - - private Path mPlaylistFolderPath; - private Path mPlaylistCurrentPath; - private Path mPlaylistBackupPath; - private Path mPlaylistLockPath; - + private UserPreferences mUserPreferences; private AtomicBoolean mPlaylistSavePending = new AtomicBoolean(); private boolean mPlaylistLoading = false; @@ -71,12 +70,14 @@ public class PlaylistManager implements ChannelEventListener * * @param channelModel */ - public PlaylistManager(AliasModel aliasModel, BroadcastModel broadcastModel, ChannelModel channelModel, ChannelMapModel channelMapModel) + public PlaylistManager(AliasModel aliasModel, BroadcastModel broadcastModel, ChannelModel channelModel, + ChannelMapModel channelMapModel, UserPreferences userPreferences) { mAliasModel = aliasModel; mBroadcastModel = broadcastModel; mChannelModel = channelModel; mChannelMapModel = channelMapModel; + mUserPreferences = userPreferences; //Register for alias, channel and channel map events so that we can //save the playlist when there are any changes @@ -164,7 +165,7 @@ public class PlaylistManager implements ChannelEventListener * and queues automatic playlist saving. */ @Override - public void channelChanged(ChannelEvent event) + public void receive(ChannelEvent event) { //Only save playlist for changes to standard channels (not traffic) if(event.getChannel().getChannelType() == ChannelType.STANDARD) @@ -180,124 +181,50 @@ public class PlaylistManager implements ChannelEventListener } } - /** - * Folder where playlist, backup and lock file are stored - */ - private Path getPlaylistFolderPath() - { - if(mPlaylistFolderPath == null) - { - SystemProperties props = SystemProperties.getInstance(); - - mPlaylistFolderPath = props.getApplicationFolder("playlist"); - } - - return mPlaylistFolderPath; - } - - /** - * Path to current playlist - */ - private Path getPlaylistPath() - { - if(mPlaylistCurrentPath == null) - { - //Temporarily removed and hard-coding the V2 playlist name. Will be reimplemented with future - //enhancement that allows multiple playlists. -// SystemProperties props = SystemProperties.getInstance(); -// String playlistDefault = props.get("playlist.defaultfilename", "playlist_v2.xml"); -// String playlistCurrent = props.get("playlist.currentfilename", playlistDefault); - - mPlaylistCurrentPath = getPlaylistFolderPath().resolve("playlist_v2.xml"); - } - - return mPlaylistCurrentPath; - } - - /** - * Path to most recent playlist backup - */ - private Path getPlaylistBackupPath() - { - if(mPlaylistBackupPath == null) - { - SystemProperties props = SystemProperties.getInstance(); - - String playlistDefault = props.get("playlist.defaultfilename", "playlist_v2.xml"); - - String playlistCurrent = props.get("playlist.currentfilename", playlistDefault); - - String playlistBackup = playlistCurrent.replace(".xml", ".bak"); - - mPlaylistBackupPath = getPlaylistFolderPath().resolve(playlistBackup); - } - - return mPlaylistBackupPath; - } - - /** - * Path to playlist lock file that is created prior to saving a playlist and removed immediately thereafter. - * Presence of a lock file indicates an incomplete or corrupt playlist file on startup. - */ - private Path getPlaylistLockPath() - { - if(mPlaylistLockPath == null) - { - SystemProperties props = SystemProperties.getInstance(); - - String playlistDefault = props.get("playlist.defaultfilename", "playlist_v2.xml"); - - String playlistCurrent = props.get("playlist.currentfilename", playlistDefault); - - String playlistLock = playlistCurrent.replace(".xml", ".lock"); - - mPlaylistLockPath = getPlaylistFolderPath().resolve(playlistLock); - } - - return mPlaylistLockPath; - } - /** * Saves the current playlist */ private void save() { + FilePreferences files = mUserPreferences.getFilePreferences(); + PlaylistV2 playlist = new PlaylistV2(); playlist.setAliases(mAliasModel.getAliases()); playlist.setBroadcastConfigurations(mBroadcastModel.getBroadcastConfigurations()); playlist.setChannels(mChannelModel.getChannels()); playlist.setChannelMaps(mChannelMapModel.getChannelMaps()); + playlist.setVersion(PLAYLIST_CURRENT_VERSION); //Create a backup copy of the current playlist - if(Files.exists(getPlaylistPath())) + if(Files.exists(files.getPlaylist())) { try { - Files.copy(getPlaylistPath(), getPlaylistBackupPath(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(files.getPlaylist(), files.getPlaylistBackup(), StandardCopyOption.REPLACE_EXISTING); } catch(Exception e) { mLog.error("Error creating backup copy of current playlist prior to saving updates [" + - getPlaylistPath().toString() + "]", e); + files.getPlaylist().toString() + "]", e); } } //Create a temporary lock file to signify that we're in the process of updating the playlist - if(!Files.exists(getPlaylistLockPath())) + if(!Files.exists(files.getPlaylistLock())) { try { - Files.createFile(getPlaylistLockPath()); + Files.createFile(files.getPlaylistLock()); } catch(IOException e) { mLog.error("Error creating temporary lock file prior to saving playlist [" + - getPlaylistLockPath().toString() + "]", e); + files.getPlaylistLock().toString() + "]", e); } } - try(OutputStream out = Files.newOutputStream(getPlaylistPath())) + try(OutputStream out = Files.newOutputStream(files.getPlaylist())) { JacksonXmlModule xmlModule = new JacksonXmlModule(); xmlModule.setDefaultUseWrapper(false); @@ -307,18 +234,20 @@ public class PlaylistManager implements ChannelEventListener out.flush(); //Remove the playlist lock file to indicate that we successfully saved the file - if(Files.exists(getPlaylistLockPath())) + if(Files.exists(files.getPlaylistLock())) { - Files.delete(getPlaylistLockPath()); + Files.delete(files.getPlaylistLock()); } + + mUserPreferences.getFilePreferences().setPlaylistLastAccessedPath(files.getPlaylist()); } catch(IOException ioe) { - mLog.error("IO error while writing the playlist to a file [" + getPlaylistPath().toString() + "]", ioe); + mLog.error("IO error while writing the playlist to a file [" + files.getPlaylist().toString() + "]", ioe); } catch(Exception e) { - mLog.error("Error while saving playlist [" + getPlaylistPath().toString() + "]", e); + mLog.error("Error while saving playlist [" + files.getPlaylist().toString() + "]", e); } } @@ -327,26 +256,28 @@ public class PlaylistManager implements ChannelEventListener */ public PlaylistV2 load() { - mLog.info("Attempting to load version 2 playlist file [" + getPlaylistPath().toString() + "]"); + FilePreferences files = mUserPreferences.getFilePreferences(); PlaylistV2 playlist = null; //Check for a lock file that indicates the previous save attempt was incomplete or had an error - if(Files.exists(getPlaylistLockPath())) + if(Files.exists(files.getPlaylistLock())) { + mLog.info("Previous playlist save was incomplete -- restoring from backup file (if possible)"); + try { //Remove the previous playlist - Files.delete(getPlaylistPath()); + Files.delete(files.getPlaylist()); //Copy the backup file to restore the previous playlist - if(Files.exists(getPlaylistBackupPath())) + if(Files.exists(files.getPlaylistBackup())) { - Files.copy(getPlaylistBackupPath(), getPlaylistPath()); + Files.copy(files.getPlaylistBackup(), files.getPlaylist()); } //Remove the lock file - Files.delete(getPlaylistLockPath()); + Files.delete(files.getPlaylistLock()); } catch(IOException ioe) { @@ -355,13 +286,15 @@ public class PlaylistManager implements ChannelEventListener } } - if(Files.exists(getPlaylistPath())) + if(Files.exists(files.getPlaylist())) { + mLog.info("Loading playlist file [" + files.getPlaylist().toString() + "]"); + JacksonXmlModule xmlModule = new JacksonXmlModule(); xmlModule.setDefaultUseWrapper(false); ObjectMapper objectMapper = new XmlMapper(xmlModule); - try(InputStream in = Files.newInputStream(getPlaylistPath())) + try(InputStream in = Files.newInputStream(files.getPlaylist())) { playlist = objectMapper.readValue(in, PlaylistV2.class); } @@ -370,9 +303,39 @@ public class PlaylistManager implements ChannelEventListener mLog.error("IO error while reading playlist file", ioe); } } + else if(Files.exists(files.getLegacyPlaylist())) + { + mLog.info("Loading legacy playlist file [" + files.getLegacyPlaylist().toString() + "]"); + + JacksonXmlModule xmlModule = new JacksonXmlModule(); + xmlModule.setDefaultUseWrapper(false); + ObjectMapper objectMapper = new XmlMapper(xmlModule); + + try(InputStream in = Files.newInputStream(files.getLegacyPlaylist())) + { + playlist = objectMapper.readValue(in, PlaylistV2.class); + + //Perform any updates that may be needed for the playist. + if(PlaylistUpdater.update(playlist)) + { + mLog.info("Legacy playlist was updated to version [" + PLAYLIST_CURRENT_VERSION + "] - saving"); + schedulePlaylistSave(); + } + } + catch(IOException ioe) + { + mLog.error("IO error while reading playlist file", ioe); + } + } else { - mLog.info("PlaylistManager - playlist not found at [" + getPlaylistPath().toString() + "]"); + mLog.info("PlaylistManager - playlist not found at [" + files.getPlaylist().toString() + "] - creating new (empty) playlist"); + } + + if(playlist == null) + { + playlist = new PlaylistV2(); + schedulePlaylistSave(); } return playlist; diff --git a/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java b/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java new file mode 100644 index 00000000..0b907961 --- /dev/null +++ b/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java @@ -0,0 +1,452 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.playlist; + +import io.github.dsheirer.alias.Alias; +import io.github.dsheirer.alias.id.AliasID; +import io.github.dsheirer.alias.id.AliasIDType; +import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID; +import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID; +import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID; +import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID; +import io.github.dsheirer.alias.id.mobileID.Min; +import io.github.dsheirer.alias.id.talkgroup.Talkgroup; +import io.github.dsheirer.controller.channel.Channel; +import io.github.dsheirer.module.log.EventLogType; +import io.github.dsheirer.module.log.config.EventLogConfiguration; +import io.github.dsheirer.protocol.Protocol; +import io.github.dsheirer.record.RecorderType; +import io.github.dsheirer.record.config.RecordConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Playlist updater will automatically update a playlist from version 1 up to the current version specified by the + * Playlist manager + */ +public class PlaylistUpdater +{ + private final static Logger mLog = LoggerFactory.getLogger(PlaylistUpdater.class); + private static final String APCO25_RADIO_ID = "[A-Fa-f\\d\\*]{6}"; + private static final String APCO25_TALKGROUP = "[A-Fa-f\\d\\*]{4}"; + private static final String FLEETSYNC_TALKGROUP = "(\\d{3})-(\\d{4})"; + private static final String LTR_TALKGROUP = "([\\d\\*]{1})-([\\d\\*]{2})-([\\d\\*]{3})"; + private static final String LTR_TALKGROUP_WILDCARD = "\\*-\\*{2}-([\\d]{3})"; + private static final String MDC1200_TALKGROUP = "[A-Fa-f\\d\\*]{4}"; + private static final String MOBILE_ID_NUMBER = "[A-Fa-f\\d\\*]{6}"; + private static final String MPT1327_TALKGROUP = "(\\d{3})-(\\d{4})"; + private static final String PASSPORT_TALKGROUP = "[\\d\\*]{5}"; + private static final Pattern FLEETSYNC_PATTERN = Pattern.compile(FLEETSYNC_TALKGROUP); + private static final Pattern LTR_PATTERN = Pattern.compile(LTR_TALKGROUP); + private static final Pattern MPT1327_PATTERN = Pattern.compile(MPT1327_TALKGROUP); + + /** + * Updates the playlist as necessary. + * + * @param playlist to update + * @return true if the playlist was updated + */ + public static boolean update(PlaylistV2 playlist) + { + boolean updated = false; + + switch(playlist.getVersion()) + { + case 1: + mLog.info("Updating playlist from version [1] to version [" + PlaylistManager.PLAYLIST_CURRENT_VERSION + "]"); + removeVersion1AudioRecordType(playlist); + removeVersion1BinaryMessageLogger(playlist); + removeVersion1NonRecordableAliasIdentifiers(playlist); + updateVersion1Talkgroups(playlist); + updated = true; + break; + } + + + return updated; + } + + /** + * Removes version 1 AUDIO record type from the record configuration + */ + private static void removeVersion1AudioRecordType(PlaylistV2 playlist) + { + int removed = 0; + + for(Channel channel : playlist.getChannels()) + { + RecordConfiguration recordConfiguration = channel.getRecordConfiguration(); + + if(recordConfiguration.contains(RecorderType.AUDIO)) + { + Iterator it = recordConfiguration.getRecorders().iterator(); + + while(it.hasNext()) + { + if(it.next() == RecorderType.AUDIO) + { + it.remove(); + removed++; + } + } + } + } + + if(removed > 0) + { + mLog.info("Removed audio-record setting from [" + removed + "] channels. Audio recording is now a setting for each individual alias"); + } + } + + /** + * Removes version 1 BINARY message event logger + */ + private static void removeVersion1BinaryMessageLogger(PlaylistV2 playlist) + { + int removed = 0; + + for(Channel channel : playlist.getChannels()) + { + EventLogConfiguration eventLogConfiguration = channel.getEventLogConfiguration(); + + if(eventLogConfiguration.getLoggers().contains(EventLogType.BINARY_MESSAGE)) + { + Iterator it = eventLogConfiguration.getLoggers().iterator(); + + while(it.hasNext()) + { + if(it.next() == EventLogType.BINARY_MESSAGE) + { + it.remove(); + removed++; + } + } + } + } + + if(removed > 0) + { + mLog.info("Removed binary message event logging from [" + removed + + "] channels. Use demodulated bitstream recorder instead"); + } + } + + /** + * Removes non-recordable alias identifiers from all aliases. These have been replaced with the 'Record' alias + * identifier. + */ + private static void removeVersion1NonRecordableAliasIdentifiers(PlaylistV2 playlist) + { + int removed = 0; + + for(Alias alias : playlist.getAliases()) + { + Iterator it = alias.getId().iterator(); + + while(it.hasNext()) + { + if(it.next().getType() == AliasIDType.NON_RECORDABLE) + { + it.remove(); + removed++; + } + } + } + + if(removed > 0) + { + mLog.info("Removed [" + removed + "] non-recordable alias identifiers from aliases"); + } + } + + /** + * Converts legacy version 1talkgroups to the new talkgroup format. Previously, each protocol had a talkgroup + * flavor and now there is a generic talkgroup identifier with a protocol specifier. + * + * @param playlist to update + */ + private static void updateVersion1Talkgroups(PlaylistV2 playlist) + { + int updated = 0; + int notUpdated = 0; + + AliasID next = null; + List toAdd = new ArrayList<>(); + + for(Alias alias : playlist.getAliases()) + { + Iterator it = alias.getId().iterator(); + + while(it.hasNext()) + { + next = it.next(); + + switch(next.getType()) + { + case LEGACY_TALKGROUP: + LegacyTalkgroupID legacyTalkgroupId = (LegacyTalkgroupID)next; + + String talkgroup = legacyTalkgroupId.getTalkgroup(); + + if(talkgroup != null) + { + if(talkgroup.matches(APCO25_TALKGROUP) || talkgroup.matches(APCO25_RADIO_ID)) + { + if(talkgroup.contains("*")) + { + notUpdated++; + } + else + { + try + { + Integer value = Integer.parseInt(talkgroup, 16); + it.remove(); + toAdd.add(new Talkgroup(Protocol.APCO25, value)); + updated++; + } + catch(Exception e) + { + //Unable to parse integer value from talkgroup .. do nothing + notUpdated++; + } + } + } + else if(talkgroup.matches(LTR_TALKGROUP)) + { + if(talkgroup.contains("*")) + { + if(talkgroup.matches(LTR_TALKGROUP_WILDCARD)) + { + try + { + Integer group = Integer.parseInt(talkgroup.substring(5)); + + for(int area = 0; area <= 1; area++) + { + for(int channel = 1; channel <= 20; channel++) + { + int value = (area << 13) + (channel << 8) + group; + toAdd.add(new Talkgroup(Protocol.LTR, value)); + mLog.debug("LTR Talkgroup [" + talkgroup + "] updated to [" + value + "]"); + } + } + it.remove(); + updated++; + } + catch(Exception e) + { + notUpdated++; + } + } + else + { + notUpdated++; + } + } + else + { + try + { + Matcher m = LTR_PATTERN.matcher(talkgroup); + + if(m.matches()) + { + int value = Integer.valueOf(m.group(3)); + value += (Integer.valueOf(m.group(2)) << 8); + value += (Integer.valueOf(m.group(1)) << 13); + + toAdd.add(new Talkgroup(Protocol.LTR, value)); + it.remove(); + updated++; + } + } + catch(Exception e) + { + notUpdated++; + } + } + } + else if(talkgroup.matches(PASSPORT_TALKGROUP)) + { + if(talkgroup.contains("*")) + { + notUpdated++; + } + else + { + try + { + Integer value = Integer.parseInt(talkgroup); + it.remove(); + toAdd.add(new Talkgroup(Protocol.PASSPORT, value)); + updated++; + } + catch(Exception e) + { + //Unable to parse integer value from talkgroup .. do nothing + notUpdated++; + } + } + } + } + else + { + //Throw away any legacy talkgroup identifiers with a null value + it.remove(); + } + break; + case FLEETSYNC: + String fleetsync = ((FleetsyncID)next).getIdent(); + + try + { + Matcher matcher = FLEETSYNC_PATTERN.matcher(fleetsync); + + if(matcher.matches()) + { + int value = Integer.valueOf(matcher.group(2)); + value += (Integer.valueOf(matcher.group(1)) << 12); + + toAdd.add(new Talkgroup(Protocol.FLEETSYNC, value)); + it.remove(); + updated++; + } + else + { + notUpdated++; + } + } + catch(Exception e) + { + notUpdated++; + } + break; + case MDC1200: + String mdc = ((MDC1200ID)next).getIdent(); + + if(mdc != null && mdc.matches(MDC1200_TALKGROUP)) + { + try + { + int mdcValue = Integer.parseInt(mdc, 16); + toAdd.add(new Talkgroup(Protocol.MDC1200, mdcValue)); + it.remove(); + updated++; + } + catch(Exception e) + { + notUpdated++; + } + } + else + { + notUpdated++; + } + + break; + case MPT1327: + String mpt = ((MPT1327ID)next).getIdent(); + + if(mpt != null && mpt.matches(MPT1327_TALKGROUP)) + { + try + { + Matcher matcher = MPT1327_PATTERN.matcher(mpt); + + if(matcher.matches()) + { + int value = Integer.valueOf(matcher.group(2)); + value += (Integer.valueOf(matcher.group(1)) << 13); + + toAdd.add(new Talkgroup(Protocol.MPT1327, value)); + it.remove(); + updated++; + + mLog.debug("MPT-1327 Talkgroup [" + mpt + "] updated to [" + value + "]"); + } + else + { + notUpdated++; + } + } + catch(Exception e) + { + notUpdated++; + } + } + break; + case MIN: + String min = ((Min)next).getMin(); + + if(min.matches(MOBILE_ID_NUMBER)) + { + if(min.contains("*")) + { + notUpdated++; + } + else + { + try + { + Integer value = Integer.parseInt(min, 16); + it.remove(); + toAdd.add(new Talkgroup(Protocol.PASSPORT, value)); + updated++; + } + catch(Exception e) + { + //Unable to parse integer value from talkgroup .. do nothing + notUpdated++; + } + } + } + break; + } + } + + //Add any alias identifiers that were converted. We have to do this here in order + //to avoid concurrent modification errors on the alias id list. + for(AliasID aliasID : toAdd) + { + alias.addAliasID(aliasID); + } + + toAdd.clear(); + } + + if(updated > 0) + { + mLog.info("Updated [" + updated + "] legacy talkgroup identifiers to new talkgroup format"); + } + + if(notUpdated > 0) + { + mLog.info("Unable to update [" + notUpdated + "] legacy talkgroup identifiers - please edit and convert to new talkgroup format"); + } + } +} diff --git a/src/main/java/io/github/dsheirer/playlist/PlaylistV2.java b/src/main/java/io/github/dsheirer/playlist/PlaylistV2.java index f132ca3b..1091d59d 100644 --- a/src/main/java/io/github/dsheirer/playlist/PlaylistV2.java +++ b/src/main/java/io/github/dsheirer/playlist/PlaylistV2.java @@ -30,6 +30,7 @@ import java.util.List; @JacksonXmlRootElement(localName = "playlist") public class PlaylistV2 { + private int mVersion = 1; private List mAliases = new ArrayList<>(); private List mBroadcastConfigurations = new ArrayList<>(); private List mChannels = new ArrayList<>(); @@ -39,6 +40,23 @@ public class PlaylistV2 { } + /** + * Version number for this playlist + */ + @JacksonXmlProperty(isAttribute = true, localName = "version") + public int getVersion() + { + return mVersion; + } + + /** + * Sets the version for this playlist. + */ + public void setVersion(int version) + { + mVersion = version; + } + @JacksonXmlProperty(isAttribute = false, localName = "alias") public List getAliases() { diff --git a/src/main/java/io/github/dsheirer/preference/Preference.java b/src/main/java/io/github/dsheirer/preference/Preference.java new file mode 100644 index 00000000..b6003d24 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/Preference.java @@ -0,0 +1,52 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference; + +import io.github.dsheirer.sample.Listener; + +/** + * Preference base class with support for broadcasting preference changes + */ +public abstract class Preference +{ + private Listener mPreferenceUpdateListener; + + public Preference(Listener updateListener) + { + mPreferenceUpdateListener = updateListener; + } + + /** + * Indicates which type of preference for this class + */ + public abstract PreferenceType getPreferenceType(); + + /** + * Notifies any registered listeners that a preference has been updated + */ + public void notifyPreferenceUpdated() + { + if(mPreferenceUpdateListener != null) + { + mPreferenceUpdateListener.receive(getPreferenceType()); + } + } +} diff --git a/src/main/java/io/github/dsheirer/preference/PreferenceType.java b/src/main/java/io/github/dsheirer/preference/PreferenceType.java new file mode 100644 index 00000000..7a92c50e --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/PreferenceType.java @@ -0,0 +1,31 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference; + +/** + * Types of preferences + */ +public enum PreferenceType +{ + DECODE_EVENT, + IDENTIFIER, + FILES; +} diff --git a/src/main/java/io/github/dsheirer/preference/TimestampFormat.java b/src/main/java/io/github/dsheirer/preference/TimestampFormat.java new file mode 100644 index 00000000..5950594b --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/TimestampFormat.java @@ -0,0 +1,81 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference; + +import java.text.SimpleDateFormat; + +/** + * Timestamp formats + */ +public enum TimestampFormat +{ + TIMESTAMP_DEFAULT("yyyy-MM-dd HH:mm:ss", "Timestamp: yyyy-MM-dd HH:mm:ss"), + TIMESTAMP_COLONS("yyyy:MM:dd:HH:mm:ss", "Timestamp Colons: yyyy:MM:dd:HH:mm:ss"), + TIMESTAMP_MILLIS("yyyy:MM:dd:HH:mm:ss.SSS", "Timestamp Milliseconds: yyyy:MM:dd:HH:mm:ss.SSS"), + TIMESTAMP_COMPACT("yyyyMMddHHmmss", "Timestamp Compact: yyyyMMddHHmmss"), + DATE_DASHES("yyyy-MM-dd", "Date Dashes: YYYY-MM-DD"), + DATE_COLONS("yyyy:MM:dd", "Date Colons: YYYY:MM:DD"), + DATE_COMPACT("yyyyMMdd", "Date Compact: YYYYMMDD"), + TIME_COMPACT("HHmmss", "Time Compact: HHmmss"), + TIME_COLONS("HH:mm:ss", "Time Colons: HH:mm:ss"), + TIME_MILLIS("HH:mm:ss.SSS", "Time Milliseconds: HH:mm:ss.SSS"), + TIME_SECONDS("ss.SSS", "Time Seconds: ss.SSS"), + ; + + private String mFormat; + private String mLabel; + + /** + * Constructs a timestamp format + * @param format for the timestamp + * @param label to describe the format + */ + TimestampFormat(String format, String label) + { + mFormat = format; + mLabel = label; + } + + /** + * Format string for the timestamp format + */ + public String getFormat() + { + return mFormat; + } + + /** + * Creates a new Simple Date Formatter to use format string. + */ + public SimpleDateFormat getFormatter() + { + return new SimpleDateFormat(mFormat); + } + + /** + * Overrides the toString() with a descriptive label + */ + @Override + public String toString() + { + return mLabel; + } +} diff --git a/src/main/java/io/github/dsheirer/preference/UserPreferences.java b/src/main/java/io/github/dsheirer/preference/UserPreferences.java new file mode 100644 index 00000000..370d58f4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/UserPreferences.java @@ -0,0 +1,91 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference; + +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.preference.event.DecodeEventPreference; +import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference; +import io.github.dsheirer.preference.playlist.FilePreferences; +import io.github.dsheirer.sample.Listener; + +/** + * User Preferences. A collection of preferences that can be accessed by preference type. + */ +public class UserPreferences implements Listener +{ + private DecodeEventPreference mDecodeEventPreference; + private FilePreferences mFilePreferences; + private TalkgroupFormatPreference mTalkgroupFormatPreference; + + /** + * Constructs a new user preferences instance + */ + public UserPreferences() + { + loadPreferenceTypes(); + } + + /** + * Decode Event preferences + */ + public DecodeEventPreference getDecodeEventPreference() + { + return mDecodeEventPreference; + } + + /** + * Playlist preferences + */ + public FilePreferences getFilePreferences() + { + return mFilePreferences; + } + + /** + * Identifier preferences + */ + public TalkgroupFormatPreference getTalkgroupFormatPreference() + { + return mTalkgroupFormatPreference; + } + + /** + * Loads the managed preferences + */ + private void loadPreferenceTypes() + { + mDecodeEventPreference = new DecodeEventPreference(this); + mFilePreferences = new FilePreferences(this::receive); + mTalkgroupFormatPreference = new TalkgroupFormatPreference(this); + } + + /** + * Primary method for receiving notification from a managed preference that a preference type + * has been changed/updated. + * + * @param preferenceType that is updated + */ + @Override + public void receive(PreferenceType preferenceType) + { + MyEventBus.getEventBus().post(preferenceType); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/event/DecodeEventPreference.java b/src/main/java/io/github/dsheirer/preference/event/DecodeEventPreference.java new file mode 100644 index 00000000..0b078619 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/event/DecodeEventPreference.java @@ -0,0 +1,90 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.event; + +import io.github.dsheirer.preference.Preference; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.TimestampFormat; +import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.prefs.Preferences; + +/** + * User preferences for the display of channel decode events + */ +public class DecodeEventPreference extends Preference +{ + private final static Logger mLog = LoggerFactory.getLogger(DecodeEventPreference.class); + private Preferences mPreferences = Preferences.userNodeForPackage(DecodeEventPreference.class); + private TimestampFormat mTimestampFormat = TimestampFormat.TIMESTAMP_DEFAULT; + private static final String TIMESTAMP_FORMAT_KEY = "timestamp.format"; + + public DecodeEventPreference(Listener updateListener) + { + super(updateListener); + loadSettings(); + } + + @Override + public PreferenceType getPreferenceType() + { + return PreferenceType.DECODE_EVENT; + } + + private void loadSettings() + { + String format = mPreferences.get(TIMESTAMP_FORMAT_KEY, TimestampFormat.TIMESTAMP_COLONS.name()); + + if(format != null && !format.isEmpty()) + { + try + { + mTimestampFormat = TimestampFormat.valueOf(format); + } + catch(Exception e) + { + mLog.error("Error loading decode event timestamp format [" + format + "]"); + } + } + } + + /** + * Timestamp format to use for decode event timestamps + */ + public TimestampFormat getTimestampFormat() + { + return mTimestampFormat; + } + + /** + * Updates the timestamp format preference + * + * @param timestampFormat + */ + public void setTimestampFormat(TimestampFormat timestampFormat) + { + mTimestampFormat = timestampFormat; + mPreferences.put(TIMESTAMP_FORMAT_KEY, mTimestampFormat.name()); + notifyPreferenceUpdated(); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/IntegerFormat.java b/src/main/java/io/github/dsheirer/preference/identifier/IntegerFormat.java new file mode 100644 index 00000000..bdcf1b9d --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/IntegerFormat.java @@ -0,0 +1,43 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier; + +/** + * Options for formatting of integer values. + */ +public enum IntegerFormat +{ + DECIMAL("Decimal"), + FORMATTED("Formatted"), + HEXADECIMAL("Hexadecimal"); + + private String mLabel; + + IntegerFormat(String label) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java b/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java new file mode 100644 index 00000000..fef1e13d --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java @@ -0,0 +1,335 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier; + +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.patch.PatchGroupIdentifier; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.preference.Preference; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.LtrNetTalkgroupFormatter; +import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormatter; +import io.github.dsheirer.protocol.Protocol; +import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.prefs.Preferences; + +/** + * Specifies and applies user preferences for formatting identifiers + */ +public class TalkgroupFormatPreference extends Preference +{ + private final static Logger mLog = LoggerFactory.getLogger(TalkgroupFormatPreference.class); + private Preferences mPreferences = Preferences.userNodeForPackage(TalkgroupFormatPreference.class); + + public static final String TALKGROUP_FORMAT_PROPERTY = "talkgroup.format."; + public static final String TALKGROUP_FIXED_WIDTH_PROPERTY = "talkgroup.fixed.width."; + + private Map mTalkgroupFormatProtocolMap = new HashMap<>(); + private Map mTalkgroupFixedWidthProtocolMap = new HashMap<>(); + + /** + * Constructs an instance of identifier formatting preference. + * + * @param updateListener to be notified when this preference is updated + */ + public TalkgroupFormatPreference(Listener updateListener) + { + super(updateListener); + loadProperties(); + } + + /** + * Formats an identifier + * + * @param identifier to format + * @return formatted string representing the identifier or null if the identifier is null + */ + public String format(Identifier identifier) + { + if(identifier == null) + { + return null; + } + + switch(identifier.getForm()) + { + case TALKGROUP: + if(identifier instanceof TalkgroupIdentifier) + { + return formatTalkgroupIdentifier((TalkgroupIdentifier)identifier); + } + break; + case PATCH_GROUP: + if(identifier instanceof PatchGroupIdentifier) + { + return formatPatchGroupIdentifier((PatchGroupIdentifier)identifier); + } + break; + } + + return identifier.toString(); + } + + @Override + public PreferenceType getPreferenceType() + { + return PreferenceType.IDENTIFIER; + } + + /** + * Loads persisted properties on startup + */ + private void loadProperties() + { + for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS) + { + mTalkgroupFixedWidthProtocolMap.put(protocol, mPreferences.getBoolean(getTalkgroupFixedWidthProperty(protocol), + getDefaultFixedWidth(protocol))); + + try + { + String format = mPreferences.get(getTalkgroupFormatProperty(protocol), getDefaultFormat(protocol).name()); + mTalkgroupFormatProtocolMap.put(protocol, IntegerFormat.valueOf(format)); + } + catch(Exception e) + { + //If there is a problem parsing the enum entry, default to DECIMAL + mTalkgroupFormatProtocolMap.put(protocol, getDefaultFormat(protocol)); + } + } + } + + /** + * Default format for each protocol + */ + private IntegerFormat getDefaultFormat(Protocol protocol) + { + switch(protocol) + { + case APCO25: + return IntegerFormat.DECIMAL; + case FLEETSYNC: + return IntegerFormat.FORMATTED; + case IPV4: + return IntegerFormat.DECIMAL; + case LOJACK: + return IntegerFormat.DECIMAL; + case LTR: + return IntegerFormat.FORMATTED; + case LTR_NET: + return IntegerFormat.FORMATTED; + case LTR_STANDARD: + return IntegerFormat.FORMATTED; + case MDC1200: + return IntegerFormat.DECIMAL; + case MPT1327: + return IntegerFormat.FORMATTED; + case PASSPORT: + return IntegerFormat.DECIMAL; + case TAIT1200: + return IntegerFormat.DECIMAL; + case UNKNOWN: + default: + return IntegerFormat.DECIMAL; + } + } + + private boolean getDefaultFixedWidth(Protocol protocol) + { + switch(protocol) + { + case APCO25: + return true; + case FLEETSYNC: + return true; + case IPV4: + return true; + case LOJACK: + return true; + case LTR: + return true; + case LTR_NET: + return true; + case LTR_STANDARD: + return true; + case MDC1200: + return false; + case MPT1327: + return true; + case PASSPORT: + return false; + case TAIT1200: + return false; + case UNKNOWN: + default: + return true; + } + } + + /** + * Property identifier for talkgroup prepad for a specific protocol + */ + private String getTalkgroupFixedWidthProperty(Protocol protocol) + { + return TALKGROUP_FIXED_WIDTH_PROPERTY + protocol.name(); + } + + /** + * Property identifier for talkgroup format for a specific protocol + */ + private String getTalkgroupFormatProperty(Protocol protocol) + { + return TALKGROUP_FORMAT_PROPERTY + protocol.name(); + } + + /** + * User preference for formatting talkgroups for the specified protocol + * + * @param protocol specified + * @return format + */ + public IntegerFormat getTalkgroupFormat(Protocol protocol) + { + if(mTalkgroupFormatProtocolMap.containsKey(protocol)) + { + return mTalkgroupFormatProtocolMap.get(protocol); + } + + return IntegerFormat.DECIMAL; + } + + /** + * Sets the formatting for talkgroup identifiers by protocol + * + * @param protocol for the talkgroup + * @param talkgroupFormat + */ + public void setTalkgroupFormat(Protocol protocol, IntegerFormat talkgroupFormat) + { + IntegerFormat existing = mTalkgroupFormatProtocolMap.get(protocol); + + if(existing == null || existing != talkgroupFormat) + { + mTalkgroupFormatProtocolMap.put(protocol, talkgroupFormat); + mPreferences.put(getTalkgroupFormatProperty(protocol), talkgroupFormat.name()); + notifyPreferenceUpdated(); + } + } + + /** + * Sets the fixed-width for talkgroup identifiers by protocol + * + * @param protocol for the talkgroup + * @param fixedWidth + */ + public void setTalkgroupFormat(Protocol protocol, boolean fixedWidth) + { + mTalkgroupFixedWidthProtocolMap.put(protocol, fixedWidth); + mPreferences.putBoolean(getTalkgroupFixedWidthProperty(protocol), fixedWidth); + notifyPreferenceUpdated(); + } + + /** + * Indicates if talkgroups for the specified protocol should be prepadded to a fixed length using '0' padding characters + * + * @param protocol to prepad + * @return true if the talkgroups should be fixed length/prepadded. + */ + public boolean isTalkgroupFixedWidth(Protocol protocol) + { + Boolean fixedWidth = mTalkgroupFixedWidthProtocolMap.get(protocol); + + if(fixedWidth == null) + { + fixedWidth = false; + } + + return fixedWidth; + } + + /** + * Sets the talkgroup for the specified protocol to the fixed width argument. + * + * @param protocol to set + * @param fixedWidth true for prepadded (0) fixed width values or false for no prepadding. + */ + public void setTalkgroupFixedWidth(Protocol protocol, boolean fixedWidth) + { + if(isTalkgroupFixedWidth(protocol) != fixedWidth) + { + mTalkgroupFixedWidthProtocolMap.put(protocol, fixedWidth); + mPreferences.putBoolean(getTalkgroupFixedWidthProperty(protocol), fixedWidth); + notifyPreferenceUpdated(); + } + } + + /** + * Formats the identifier according to user specified preferences for number format and length. + * + * @param talkgroupIdentifier to format + * @return formatted talkgroups + */ + private String formatTalkgroupIdentifier(TalkgroupIdentifier talkgroupIdentifier) + { + switch(talkgroupIdentifier.getProtocol()) + { + case APCO25: + return APCO25TalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.APCO25), + isTalkgroupFixedWidth(Protocol.APCO25)); + case FLEETSYNC: + return FleetsyncTalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.FLEETSYNC), + isTalkgroupFixedWidth(Protocol.FLEETSYNC)); + case LTR_NET: + return LtrNetTalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.LTR_NET), + isTalkgroupFixedWidth(Protocol.LTR_NET)); + case MPT1327: + return MPT1327TalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.MPT1327), + isTalkgroupFixedWidth(Protocol.MPT1327)); + default: + return talkgroupIdentifier.toString(); + } + } + + /** + * Formats the identifier according to user specified preferences for number format and length. + * + * @param patchGroupIdentifier to format + * @return formatted talkgroups + */ + private String formatPatchGroupIdentifier(PatchGroupIdentifier patchGroupIdentifier) + { + switch(patchGroupIdentifier.getProtocol()) + { + case APCO25: + return APCO25TalkgroupFormatter.format(patchGroupIdentifier, getTalkgroupFormat(Protocol.APCO25), + isTalkgroupFixedWidth(Protocol.APCO25)); + default: + return patchGroupIdentifier.toString(); + } + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/APCO25TalkgroupFormatter.java b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/APCO25TalkgroupFormatter.java new file mode 100644 index 00000000..ab55c368 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/APCO25TalkgroupFormatter.java @@ -0,0 +1,98 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier.talkgroup; + +import io.github.dsheirer.identifier.patch.PatchGroup; +import io.github.dsheirer.identifier.patch.PatchGroupIdentifier; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.preference.identifier.IntegerFormat; + +public class APCO25TalkgroupFormatter extends IntegerFormatter +{ + public static final int GROUP_DECIMAL_WIDTH = 5; + public static final int UNIT_DECIMAL_WIDTH = 8; + public static final int GROUP_HEXADECIMAL_WIDTH = 4; + public static final int UNIT_HEXADECIMAL_WIDTH = 6; + + /** + * Formats the individual or group identifier to the specified format and width. + */ + public static String format(TalkgroupIdentifier identifier, IntegerFormat format, boolean fixedWidth) + { + if(fixedWidth) + { + switch(format) + { + case DECIMAL: + return toDecimal(identifier.getValue(), + (identifier.isGroup() ? GROUP_DECIMAL_WIDTH : UNIT_DECIMAL_WIDTH)); + case HEXADECIMAL: + return toHex(identifier.getValue(), + (identifier.isGroup() ? GROUP_HEXADECIMAL_WIDTH : UNIT_HEXADECIMAL_WIDTH)); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + else + { + switch(format) + { + case DECIMAL: + case FORMATTED: + return identifier.getValue().toString(); + case HEXADECIMAL: + return toHex(identifier.getValue()); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + } + + /** + * Formats the patch group to the specified format and width + */ + public static String format(PatchGroupIdentifier identifier, IntegerFormat format, boolean fixedWidth) + { + PatchGroup patchGroup = identifier.getValue(); + + if(patchGroup != null) + { + StringBuilder sb = new StringBuilder(); + sb.append("P:"); + sb.append(format(patchGroup.getPatchGroup(), format, fixedWidth)); + sb.append("["); + + int counter = 0; + for(TalkgroupIdentifier patchedGroup: patchGroup.getPatchedGroupIdentifiers()) + { + sb.append(format(patchedGroup, format, fixedWidth)); + if(counter++ < patchGroup.getPatchedGroupIdentifiers().size() - 1) + { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + return identifier.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/FleetsyncTalkgroupFormatter.java b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/FleetsyncTalkgroupFormatter.java new file mode 100644 index 00000000..86fdaef9 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/FleetsyncTalkgroupFormatter.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier.talkgroup; + +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.fleetsync2.identifier.FleetsyncIdentifier; +import io.github.dsheirer.preference.identifier.IntegerFormat; + +/** + * Formats Fleetsync 20-bit identifiers into either an integer value or an 8-bit fleet and 12-bit ident + */ +public class FleetsyncTalkgroupFormatter extends IntegerFormatter +{ + private static final int IDENTIFIER_DECIMAL_WIDTH = 7; + private static final int IDENTIFIER_HEXADECIMAL_WIDTH = 5; + private static final int FLEET_DECIMAL_WIDTH = 3; + private static final int IDENT_DECIMAL_WIDTH = 4; + private static final String SEPARATOR = "-"; + + /** + * Formats the individual or group identifier to the specified format and width. + */ + public static String format(TalkgroupIdentifier identifier, IntegerFormat format, boolean fixedWidth) + { + if(identifier instanceof FleetsyncIdentifier) + { + FleetsyncIdentifier fleetsync = (FleetsyncIdentifier)identifier; + + if(fixedWidth) + { + switch(format) + { + case DECIMAL: + return toDecimal(fleetsync.getValue(), IDENTIFIER_DECIMAL_WIDTH); + case FORMATTED: + return toDecimal(fleetsync.getFleet(), FLEET_DECIMAL_WIDTH) + SEPARATOR + + toDecimal(fleetsync.getIdent(), IDENT_DECIMAL_WIDTH); + case HEXADECIMAL: + return toHex(fleetsync.getValue(), IDENTIFIER_HEXADECIMAL_WIDTH); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + else + { + switch(format) + { + case DECIMAL: + return fleetsync.toString(); + case FORMATTED: + return fleetsync.getFleet() + SEPARATOR + fleetsync.getIdent(); + case HEXADECIMAL: + return toHex(identifier.getValue()); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + } + + return identifier.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/IntegerFormatter.java b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/IntegerFormatter.java new file mode 100644 index 00000000..2b5a71ac --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/IntegerFormatter.java @@ -0,0 +1,47 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier.talkgroup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base formatter class for integer values + */ +public class IntegerFormatter +{ + private final static Logger mLog = LoggerFactory.getLogger(IntegerFormatter.class); + + public static String toHex(int value) + { + return Integer.toHexString(value).toUpperCase(); + } + + public static String toHex(int value, int width) + { + return String.format("%0" + width + "X", value); + } + + public static String toDecimal(int value, int width) + { + return String.format("%0" + width + "d", value); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/LtrNetTalkgroupFormatter.java b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/LtrNetTalkgroupFormatter.java new file mode 100644 index 00000000..f223049f --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/LtrNetTalkgroupFormatter.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier.talkgroup; + +import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.preference.identifier.IntegerFormat; + +/** + * Formats Fleetsync 20-bit identifiers into either an integer value or an 8-bit fleet and 12-bit ident + */ +public class LtrNetTalkgroupFormatter extends IntegerFormatter +{ + private static final int IDENTIFIER_DECIMAL_WIDTH = 4; + private static final int IDENTIFIER_HEXADECIMAL_WIDTH = 4; + private static final int HOME_REPEATER_DECIMAL_WIDTH = 2; + private static final int TALKGROUP_DECIMAL_WIDTH = 3; + private static final String SEPARATOR = "-"; + + /** + * Formats the individual or group identifier to the specified format and width. + */ + public static String format(TalkgroupIdentifier identifier, IntegerFormat format, boolean fixedWidth) + { + if(identifier instanceof LTRTalkgroup) + { + LTRTalkgroup ltrnet = (LTRTalkgroup)identifier; + + if(fixedWidth) + { + switch(format) + { + case DECIMAL: + return toDecimal(ltrnet.getValue(), IDENTIFIER_DECIMAL_WIDTH); + case FORMATTED: + return toDecimal(ltrnet.getHomeChannel(), HOME_REPEATER_DECIMAL_WIDTH) + SEPARATOR + + toDecimal(ltrnet.getTalkgroup(), TALKGROUP_DECIMAL_WIDTH); + case HEXADECIMAL: + return toHex(ltrnet.getValue(), IDENTIFIER_HEXADECIMAL_WIDTH); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + else + { + switch(format) + { + case DECIMAL: + return ltrnet.toString(); + case FORMATTED: + return ltrnet.getHomeChannel() + SEPARATOR + ltrnet.getTalkgroup(); + case HEXADECIMAL: + return toHex(identifier.getValue()); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + } + + return identifier.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/MPT1327TalkgroupFormatter.java b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/MPT1327TalkgroupFormatter.java new file mode 100644 index 00000000..6723123e --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/MPT1327TalkgroupFormatter.java @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.identifier.talkgroup; + +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.mpt1327.identifier.MPT1327Talkgroup; +import io.github.dsheirer.preference.identifier.IntegerFormat; + +/** + * Formats Fleetsync 20-bit identifiers into either an integer value or an 8-bit fleet and 12-bit ident + */ +public class MPT1327TalkgroupFormatter extends IntegerFormatter +{ + private static final int IDENTIFIER_DECIMAL_WIDTH = 7; + private static final int IDENTIFIER_HEXADECIMAL_WIDTH = 5; + private static final int FLEET_DECIMAL_WIDTH = 3; + private static final int IDENT_DECIMAL_WIDTH = 4; + private static final String SEPARATOR = "-"; + + /** + * Formats the individual or group identifier to the specified format and width. + */ + public static String format(TalkgroupIdentifier identifier, IntegerFormat format, boolean fixedWidth) + { + if(identifier instanceof MPT1327Talkgroup) + { + MPT1327Talkgroup mpt = (MPT1327Talkgroup)identifier; + + if(fixedWidth) + { + switch(format) + { + case DECIMAL: + return toDecimal(mpt.getValue(), IDENTIFIER_DECIMAL_WIDTH); + case FORMATTED: + return toDecimal(mpt.getPrefix(), FLEET_DECIMAL_WIDTH) + SEPARATOR + + toDecimal(mpt.getIdent(), IDENT_DECIMAL_WIDTH); + case HEXADECIMAL: + return toHex(mpt.getValue(), IDENTIFIER_HEXADECIMAL_WIDTH); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + else + { + switch(format) + { + case DECIMAL: + return mpt.toString(); + case FORMATTED: + return mpt.getPrefix() + SEPARATOR + mpt.getIdent(); + case HEXADECIMAL: + return toHex(identifier.getValue()); + default: + throw new IllegalArgumentException("Unrecognized integer format: " + format); + } + } + } + + return identifier.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java b/src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java new file mode 100644 index 00000000..b5e715db --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java @@ -0,0 +1,279 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.playlist; + +import io.github.dsheirer.preference.Preference; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.prefs.Preferences; + +/** + * User preferences for playlists + */ +public class FilePreferences extends Preference +{ + private final static Logger mLog = LoggerFactory.getLogger(FilePreferences.class); + private Preferences mPreferences = Preferences.userNodeForPackage(FilePreferences.class); + + private static final String FOLDER_APPLICATION_ROOT = "SDRTrunk"; + private static final String FOLDER_PLAYLIST = "playlist"; + private static final String FILE_PLAYLIST = "default.xml"; + private static final String FILE_LEGACY_PLAYLIST = "playlist_v2.xml"; + + private static final String PREFERENCE_KEY_PLAYLIST_FOLDER = "playlist.folder"; + private static final String PREFERENCE_KEY_PLAYLIST_LAST_ACCESSED = "playlist.last.accessed"; + private static final String PREFERENCE_KEY_PLAYLIST_STARTUP = "playlist.default"; + private static final String PREFERENCE_KEY_PLAYLIST_USE_LAST_ACCESSED = "playlist.use.last.accessed"; + + private Path mPlaylistFolder; + private Path mPlaylistStartupPath; + private Path mPlaylistLastAccessedPath; + private Boolean mUsePlaylistLastAccessedByDefault = true; + + /** + * Constructs this preference with an update listener + * @param updateListener to receive notifications whenever these preferences change + */ + public FilePreferences(Listener updateListener) + { + super(updateListener); + } + + @Override + public PreferenceType getPreferenceType() + { + return PreferenceType.FILES; + } + + + /** + * Path to the current playlist + */ + public Path getPlaylist() + { + if(mUsePlaylistLastAccessedByDefault) + { + return getPlaylistLastAccessedPath(); + } + else + { + return getPlaylistStartupPath(); + } + } + + /** + * Legacy (version 2) playlist path. + */ + public Path getLegacyPlaylist() + { + return getPlaylistFolder().resolve(FILE_LEGACY_PLAYLIST); + } + + /** + * Lock file for the playlist when updating the file. + */ + public Path getPlaylistLock() + { + String playlist = getPlaylist().toAbsolutePath().toString(); + return Paths.get(playlist + ".lck"); + } + + /** + * Lock file for the playlist when updating the file. + */ + public Path getPlaylistBackup() + { + String playlist = getPlaylist().toString(); + return Paths.get(playlist + ".backup"); + } + + /** + * Path to the playlist to use at startup when 'use last accessed' is set to false. + */ + public Path getPlaylistStartupPath() + { + if(mPlaylistStartupPath == null) + { + mPlaylistStartupPath = getPath(PREFERENCE_KEY_PLAYLIST_STARTUP, getDefaultPlaylistPath()); + } + + return mPlaylistStartupPath; + } + + /** + * Sets the path to the default playlist + */ + public void setPlaylistStartupPath(Path path) + { + mPlaylistStartupPath = path; + mPreferences.put(PREFERENCE_KEY_PLAYLIST_STARTUP, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Path for the last accesssed playlist + */ + public Path getPlaylistLastAccessedPath() + { + if(mPlaylistLastAccessedPath == null) + { + mPlaylistLastAccessedPath = getPath(PREFERENCE_KEY_PLAYLIST_LAST_ACCESSED, getDefaultPlaylistPath()); + } + + return mPlaylistLastAccessedPath; + } + + /** + * Sets the path to the last accessed playlist + */ + public void setPlaylistLastAccessedPath(Path path) + { + mPlaylistLastAccessedPath = path; + mPreferences.put(PREFERENCE_KEY_PLAYLIST_LAST_ACCESSED, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing playlist + */ + public Path getPlaylistFolder() + { + if(mPlaylistFolder == null) + { + mPlaylistFolder = getPath(PREFERENCE_KEY_PLAYLIST_FOLDER, getDefaultPlaylistFolder()); + } + + return mPlaylistFolder; + } + + /** + * Sets the path to the playlist folder + */ + public void setPlaylistFolder(Path path) + { + mPlaylistFolder = path; + mPreferences.put(PREFERENCE_KEY_PLAYLIST_FOLDER, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Indicates the playlist to load on startup, either the last accessed playlist (true) or the default playlist (false). + */ + public boolean usePlaylistLastAccessedByDefault() + { + if(mUsePlaylistLastAccessedByDefault == null) + { + mUsePlaylistLastAccessedByDefault = mPreferences.getBoolean(PREFERENCE_KEY_PLAYLIST_USE_LAST_ACCESSED, true); + } + + return mUsePlaylistLastAccessedByDefault; + } + + /** + * Sets using the last accessed playlist by default (true) or the default playlist (false). + */ + public void setUsePlaylistLastAccessedByDefault(boolean useLastAccessed) + { + mUsePlaylistLastAccessedByDefault = useLastAccessed; + mPreferences.putBoolean(PREFERENCE_KEY_PLAYLIST_USE_LAST_ACCESSED, useLastAccessed); + notifyPreferenceUpdated(); + } + + + /** + * Default application root path + */ + private Path getDefaultApplicationFolder() + { + Path defaultApplicationPath = Paths.get(System.getProperty("user.home"), FOLDER_APPLICATION_ROOT); + createDirectory(defaultApplicationPath); + return defaultApplicationPath; + } + + /** + * Default playlist folder + */ + public Path getDefaultPlaylistFolder() + { + Path defaultPlaylistFolder = getDefaultApplicationFolder().resolve(FOLDER_PLAYLIST); + createDirectory(defaultPlaylistFolder); + return defaultPlaylistFolder; + } + + /** + * Default playlist + */ + private Path getDefaultPlaylistPath() + { + return getDefaultPlaylistFolder().resolve(FILE_PLAYLIST); + } + + /** + * Returns the path stored in preferences and referenced by the key argument if it exists, otherwise returns the + * default path. + * + * Note: the default path is not checked for existence. + * + * @param key to the preferences service for the path + * @param defaultPath to use if the preferred path does not exist + * @return requested path + */ + private Path getPath(String key, Path defaultPath) + { + String stringPath = mPreferences.get(PREFERENCE_KEY_PLAYLIST_STARTUP, defaultPath.toString()); + + if(stringPath != null && !stringPath.isEmpty()) + { + Path temp = Paths.get(stringPath); + if(Files.exists(temp)) + { + return temp; + } + } + + return defaultPath; + } + + /** + * Creates a directory if it does not already exist + */ + private void createDirectory(Path directory) + { + if(!Files.exists(directory) && Files.isDirectory(directory)) + { + try + { + Files.createDirectory(directory); + mLog.info("Created directory [" + directory.toString() + "]"); + } + catch(Exception e) + { + mLog.error("Error creating directory [" + directory.toString() + "]"); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/protocol/Protocol.java b/src/main/java/io/github/dsheirer/protocol/Protocol.java new file mode 100644 index 00000000..d25e6a93 --- /dev/null +++ b/src/main/java/io/github/dsheirer/protocol/Protocol.java @@ -0,0 +1,72 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.protocol; + +import java.util.EnumSet; + +/** + * Binary protocols supported within this application + */ +public enum Protocol +{ + APCO25("APCO-25", "APCO25PHASE1", 9600), + FLEETSYNC("Fleetsync", "FLEETSYNC", 1200), + IPV4("IPV4", "IPV4", 0), + LOJACK("LoJack", "LOJACK", 1200), + LTR("LTR", "LTR", 300), + LTR_NET("LTR-Net", "LTRNET", 300), + LTR_STANDARD("LTR-Standard", "LTRSTANDARD", 300), + MDC1200("MDC-1200", "MDC1200", 1200), + MPT1327("MPT-1327", "MPT1327", 1200), + PASSPORT("Passport", "PASSPORT", 300), + TAIT1200("Tait 1200", "TAIT1200", 1200), + UNKNOWN("Unknown", "UNKNOWN", 0); + + private String mLabel; + private String mFileNameLabel; + private int mBitRate; + + + Protocol(String label, String fileNameLabel, int bitRate) + { + mLabel = label; + mFileNameLabel = fileNameLabel; + mBitRate = bitRate; + } + + public static EnumSet TALKGROUP_PROTOCOLS = EnumSet.of(APCO25, FLEETSYNC, LTR_NET, LTR_STANDARD, MDC1200, + MPT1327, PASSPORT, TAIT1200); + + @Override + public String toString() + { + return mLabel; + } + + public String getFileNameLabel() + { + return mFileNameLabel; + } + + public int getBitRate() + { + return mBitRate; + } +} diff --git a/src/main/java/io/github/dsheirer/record/AudioRecorder.java b/src/main/java/io/github/dsheirer/record/AudioRecorder.java index b05fea8a..b72f4f25 100644 --- a/src/main/java/io/github/dsheirer/record/AudioRecorder.java +++ b/src/main/java/io/github/dsheirer/record/AudioRecorder.java @@ -19,7 +19,7 @@ package io.github.dsheirer.record; import io.github.dsheirer.audio.IAudioPacketListener; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.module.Module; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; @@ -49,7 +49,7 @@ public abstract class AudioRecorder extends Module implements Listener { private static final long serialVersionUID = 1L; - private JCheckBox mAudioRecorder; private JCheckBox mBasebandRecorder; private JCheckBox mTrafficBasebandRecorder; private JCheckBox mBitstreamRecorder; @@ -44,18 +43,6 @@ public class RecordConfigurationEditor extends Editor { setLayout(new MigLayout("fill,wrap 2", "", "[][][][grow]")); - mAudioRecorder = new JCheckBox("Audio"); - mAudioRecorder.setEnabled(false); - mAudioRecorder.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - setModified(true); - } - }); - add(mAudioRecorder, "wrap"); - mBasebandRecorder = new JCheckBox("Baseband I/Q"); mBasebandRecorder.setEnabled(false); mBasebandRecorder.addActionListener(new ActionListener() @@ -107,11 +94,6 @@ public class RecordConfigurationEditor extends Editor private void setControlsEnabled(boolean enabled) { - if(mAudioRecorder.isEnabled() != enabled) - { - mAudioRecorder.setEnabled(enabled); - } - if(mBasebandRecorder.isEnabled() != enabled) { mBasebandRecorder.setEnabled(enabled); @@ -142,11 +124,6 @@ public class RecordConfigurationEditor extends Editor config.clearRecorders(); - if(mAudioRecorder.isSelected()) - { - config.addRecorder(RecorderType.AUDIO); - } - if(mBasebandRecorder.isSelected()) { config.addRecorder(RecorderType.BASEBAND); @@ -181,7 +158,6 @@ public class RecordConfigurationEditor extends Editor setControlsEnabled(true); List recorders = getItem().getRecordConfiguration().getRecorders(); - mAudioRecorder.setSelected(recorders.contains(RecorderType.AUDIO)); mBasebandRecorder.setSelected(recorders.contains(RecorderType.BASEBAND)); mTrafficBasebandRecorder.setSelected(recorders.contains(RecorderType.TRAFFIC_BASEBAND)); mBitstreamRecorder.setSelected(recorders.contains(RecorderType.DEMODULATED_BIT_STREAM)); diff --git a/src/main/java/io/github/dsheirer/record/RecorderManager.java b/src/main/java/io/github/dsheirer/record/RecorderManager.java index da8f0fca..59655c61 100644 --- a/src/main/java/io/github/dsheirer/record/RecorderManager.java +++ b/src/main/java/io/github/dsheirer/record/RecorderManager.java @@ -15,16 +15,26 @@ ******************************************************************************/ package io.github.dsheirer.record; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.alias.AliasModel; +import io.github.dsheirer.identifier.Form; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.IdentifierClass; +import io.github.dsheirer.identifier.IdentifierCollection; +import io.github.dsheirer.identifier.Role; +import io.github.dsheirer.identifier.integer.IntegerIdentifier; +import io.github.dsheirer.identifier.string.StringIdentifier; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.record.wave.AudioPacketWaveRecorder; import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder; +import io.github.dsheirer.record.wave.WaveMetadata; import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.OverflowableTransferQueue; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; import io.github.dsheirer.util.StringUtils; import io.github.dsheirer.util.ThreadPool; +import io.github.dsheirer.util.TimeStamp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,10 +55,12 @@ public class RecorderManager implements Listener public static final float BASEBAND_SAMPLE_RATE = 25000.0f; //Default sample rate - source can override public static final long IDLE_RECORDER_REMOVAL_THRESHOLD = 6000; //6 seconds - private Map mRecorders = new HashMap<>(); + private Map mRecorders = new HashMap<>(); private OverflowableTransferQueue mAudioPacketQueue = new OverflowableTransferQueue<>(1000, 100); private List mAudioPackets = new ArrayList<>(); private ScheduledFuture mBufferProcessorFuture; + private AliasModel mAliasModel; + private int mUnknownAudioRecordingIndex = 1; private boolean mCanStartNewRecorders = true; @@ -59,8 +71,9 @@ public class RecorderManager implements Listener * indicating that the call is complete. A separate recording monitor periodically checks for idled recorders to * be stopped for cases when the channel fails to send an end-call audio packet. */ - public RecorderManager() + public RecorderManager(AliasModel aliasModel) { + mAliasModel = aliasModel; mAudioPacketQueue.setOverflowListener(new IOverflowListener() { @Override @@ -78,7 +91,7 @@ public class RecorderManager implements Listener }); mBufferProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new BufferProcessor(), 0, - 1, TimeUnit.SECONDS); + 500, TimeUnit.MILLISECONDS); } /** @@ -100,7 +113,7 @@ public class RecorderManager implements Listener @Override public void receive(ReusableAudioPacket audioPacket) { - if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable()) + if(audioPacket.hasIdentifierCollection() && audioPacket.isRecordable()) { mAudioPacketQueue.offer(audioPacket); } @@ -121,11 +134,11 @@ public class RecorderManager implements Listener { for(ReusableAudioPacket audioPacket : mAudioPackets) { - String identifier = audioPacket.getMetadata().getUniqueIdentifier(); + int audioChannelId = audioPacket.getAudioChannelId(); - if(mRecorders.containsKey(identifier)) + if(mRecorders.containsKey(audioChannelId)) { - AudioPacketWaveRecorder recorder = mRecorders.get(identifier); + AudioPacketWaveRecorder recorder = mRecorders.get(audioChannelId); if(audioPacket.getType() == ReusableAudioPacket.Type.AUDIO) { @@ -134,27 +147,25 @@ public class RecorderManager implements Listener } else if(audioPacket.getType() == ReusableAudioPacket.Type.END) { - AudioPacketWaveRecorder finished = mRecorders.remove(identifier); - finished.stop(); + AudioPacketWaveRecorder finished = mRecorders.remove(audioChannelId); + stopRecorder(finished); } } else if(audioPacket.getType() == ReusableAudioPacket.Type.AUDIO) { if(mCanStartNewRecorders) { - String filePrefix = getFilePrefix(audioPacket); - AudioPacketWaveRecorder recorder = null; try { - recorder = new AudioPacketWaveRecorder(filePrefix, audioPacket.getMetadata()); + recorder = new AudioPacketWaveRecorder(getTemporaryFilePath(audioPacket)); recorder.start(); audioPacket.incrementUserCount(); recorder.receive(audioPacket); - mRecorders.put(identifier, recorder); + mRecorders.put(audioPacket.getAudioChannelId(), recorder); } catch(Exception ioe) { @@ -165,7 +176,7 @@ public class RecorderManager implements Listener if(recorder != null) { - recorder.stop(); + stopRecorder(recorder); } } } @@ -179,54 +190,116 @@ public class RecorderManager implements Listener } } + private void stopRecorder(AudioPacketWaveRecorder recorder) + { + Path rename = getFinalFileName(recorder.getIdentifierCollection()); + + AliasList aliasList = null; + + if(recorder.getIdentifierCollection() != null) + { + aliasList = mAliasModel.getAliasList(recorder.getIdentifierCollection()); + } + + WaveMetadata waveMetadata = WaveMetadata.createFrom(recorder.getIdentifierCollection(), aliasList); + recorder.stop(rename, waveMetadata); + } + /** * Removes recorders that have not received any new audio buffers in the last 6 seconds. */ private void removeIdleRecorders() { - Iterator> it = mRecorders.entrySet().iterator(); + Iterator> it = mRecorders.entrySet().iterator(); while(it.hasNext()) { - Map.Entry entry = it.next(); + Map.Entry entry = it.next(); if(entry.getValue().getLastBufferReceived() + IDLE_RECORDER_REMOVAL_THRESHOLD < System.currentTimeMillis()) { - mLog.info("Removing idle recorder [" + entry.getKey() + "]"); it.remove(); - entry.getValue().stop(); + stopRecorder(entry.getValue()); } } } + private Path getTemporaryFilePath(ReusableAudioPacket packet) + { + StringBuilder sb = new StringBuilder(); + sb.append(TimeStamp.getTimeStamp("-")); + sb.append("_audio_channel_"); + sb.append(packet.getAudioChannelId()); + sb.append(".tmp"); + return getRecordingBasePath().resolve(sb.toString()); + } + /** - * Constructs a file name and path for an audio recording + * Provides a formatted audio recording filename to use as the final audio filename. + * @return */ - private String getFilePrefix(ReusableAudioPacket packet) + private Path getFinalFileName(IdentifierCollection identifierCollection) { - Path baseFilePath = SystemProperties.getInstance().getApplicationFolder("recordings"); - - Metadata metadata = packet.getMetadata(); - StringBuilder sb = new StringBuilder(); - sb.append(metadata.hasChannelConfigurationSystem() ? metadata.getChannelConfigurationSystem() + "_" : ""); - sb.append(metadata.hasChannelConfigurationSite() ? metadata.getChannelConfigurationSite() + "_" : ""); - sb.append(metadata.hasChannelConfigurationName() ? metadata.getChannelConfigurationName() + "_" : ""); - if(metadata.getPrimaryAddressTo().hasIdentifier()) + if(identifierCollection != null) { - sb.append("_TO_").append(metadata.getPrimaryAddressTo().getIdentifier()); + Identifier system = identifierCollection.getIdentifier(IdentifierClass.CONFIGURATION, Form.SYSTEM, Role.ANY); - if(metadata.getPrimaryAddressFrom().hasIdentifier()) + if(system != null) { - sb.append("_FROM_").append(metadata.getPrimaryAddressFrom().getIdentifier()); + sb.append(((StringIdentifier)system).getValue()).append("_"); + } + + Identifier site = identifierCollection.getIdentifier(IdentifierClass.CONFIGURATION, Form.SITE, Role.ANY); + + if(site != null) + { + sb.append(((StringIdentifier)site).getValue()).append("_"); + } + + Identifier channel = identifierCollection.getIdentifier(IdentifierClass.CONFIGURATION, Form.CHANNEL, Role.ANY); + + if(channel != null) + { + sb.append(((StringIdentifier)channel).getValue()).append("_"); + } + + Identifier to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO); + + if(to != null) + { + sb.append("_TO_").append(((IntegerIdentifier)to).getValue()); + } + + Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM); + + if(from != null) + { + sb.append("_FROM_").append(((IntegerIdentifier)from).getValue()); + } + } + else + { + sb.append("audio_recording_no_metadata_").append(mUnknownAudioRecordingIndex++); + + if(mUnknownAudioRecordingIndex < 0) + { + mUnknownAudioRecordingIndex = 1; } } - String cleanedFileName = StringUtils.replaceIllegalCharacters(sb.toString()); - return baseFilePath.resolve(cleanedFileName).toString(); + StringBuilder sbFinal = new StringBuilder(); + sbFinal.append(TimeStamp.getTimeStamp("_")); + + //Remove any illegal filename characters + sbFinal.append(StringUtils.replaceIllegalCharacters(sb.toString())); + sbFinal.append(".wav"); + + return getRecordingBasePath().resolve(sbFinal.toString()); } + public Path getRecordingBasePath() { return SystemProperties.getInstance().getApplicationFolder("recordings"); @@ -253,8 +326,15 @@ public class RecorderManager implements Listener @Override public void run() { - processBuffers(); - removeIdleRecorders(); + try + { + processBuffers(); + removeIdleRecorders(); + } + catch(Throwable t) + { + mLog.error("Error while processing audio buffers", t); + } } } } diff --git a/src/main/java/io/github/dsheirer/record/RecorderType.java b/src/main/java/io/github/dsheirer/record/RecorderType.java index 1921cad4..2bfa12f3 100644 --- a/src/main/java/io/github/dsheirer/record/RecorderType.java +++ b/src/main/java/io/github/dsheirer/record/RecorderType.java @@ -23,7 +23,7 @@ import java.util.EnumSet; public enum RecorderType { /** - * Audio - 8kHz PCM + * Audio - 8kHz PCM -- **DO NOT USE - DEPRECATED - AUDIO RECORDING IS NOT MANAGED BY ALIASES */ AUDIO( "Audio (.wav)"), @@ -52,7 +52,7 @@ public enum RecorderType /** * Recorders available to all decoders */ - public static final EnumSet DEFAULT_RECORDER_TYPES = EnumSet.of(AUDIO, BASEBAND, TRAFFIC_BASEBAND); + public static final EnumSet DEFAULT_RECORDER_TYPES = EnumSet.of(BASEBAND, TRAFFIC_BASEBAND); RecorderType( String displayString ) { diff --git a/src/main/java/io/github/dsheirer/record/binary/BinaryReader.java b/src/main/java/io/github/dsheirer/record/binary/BinaryReader.java new file mode 100644 index 00000000..e4454b9c --- /dev/null +++ b/src/main/java/io/github/dsheirer/record/binary/BinaryReader.java @@ -0,0 +1,215 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ +package io.github.dsheirer.record.binary; + +import io.github.dsheirer.sample.buffer.ReusableByteBuffer; +import io.github.dsheirer.sample.buffer.ReusableByteBufferQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BinaryReader implements Iterator, AutoCloseable +{ + private final static Logger mLog = LoggerFactory.getLogger(BinaryReader.class); + private ReusableByteBufferQueue mBufferQueue = new ReusableByteBufferQueue("Binary Reader"); + private int mBufferSize; + private InputStream mInputStream; + private Path mPath; + private ReusableByteBuffer mNextBuffer; + private TimestampTracker mTimestampTracker = new TimestampTracker(); + + /** + * Constructs a binary reader + * @param path to the binary file + * @param bufferSize for each buffer read from the file + * @throws IOException + */ + public BinaryReader(Path path, int bufferSize) throws IOException + { + mBufferSize = bufferSize; + mPath = path; + mInputStream = new BufferedInputStream(new FileInputStream(path.toFile())); + mTimestampTracker.processFileName(path.toString()); + getNext(); + } + + @Override + public void close() throws Exception + { + if(mInputStream != null) + { + mInputStream.close(); + mInputStream = null; + mPath = null; + } + } + + @Override + public boolean hasNext() + { + return mNextBuffer != null; + } + + /** + * Returns a full reusable byte buffer, or at the end of the file this method may return a + * reusable byte buffer that is less than the requested buffer size, containing the remaining + * bytes from the file. + * + * @return + */ + @Override + public ReusableByteBuffer next() + { + ReusableByteBuffer current = mNextBuffer; + getNext(); + return current; + } + + /** + * Loads the next buffer + */ + private void getNext() + { + try + { + mNextBuffer = mBufferQueue.getBuffer(mBufferSize); + + int bytesRead = mInputStream.read(mNextBuffer.getBytes(), 0, mBufferSize); + + mNextBuffer.setTimestamp(mTimestampTracker.getTimestamp()); + mTimestampTracker.updateBytesProcessed(bytesRead); + + if(bytesRead > 0) + { + if(bytesRead < mBufferSize) + { + ReusableByteBuffer partialBuffer = mBufferQueue.getBuffer(bytesRead); + System.arraycopy(mNextBuffer.getBytes(), 0, partialBuffer.getBytes(), 0, bytesRead); + partialBuffer.setTimestamp(mNextBuffer.getTimestamp()); + mNextBuffer.decrementUserCount(); + mNextBuffer = partialBuffer; + } + } + else + { + mNextBuffer.decrementUserCount(); + mNextBuffer = null; + } + } + catch(IOException e) + { + mLog.error("Error reading binary file [" + mPath.toString() + "]", e); + mNextBuffer.decrementUserCount(); + mNextBuffer = null; + } + } + + public class TimestampTracker + { + private final Pattern TIMESTAMP_BITRATE_PATTERN = Pattern.compile(".*(\\d{8}_\\d{6})_(\\d{4,8})BPS_.*.bits"); + private final SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMdd_HHmmss"); + private Double mBitRate = null; + private long mTimestamp; + + public TimestampTracker() + { + } + + /** + * Processes the file name to attempt to extract the file starting timestamp and bit rate. + * @param filename + */ + public void processFileName(String filename) + { + try + { + Matcher m = TIMESTAMP_BITRATE_PATTERN.matcher(filename); + + if(m.find()) + { + long timestamp = SDF.parse(m.group(1)).getTime(); + int bitRate = Integer.parseInt(m.group(2)); + + setTimestamp(timestamp); + setBitRate(bitRate); + return; + } + } + catch(Exception e) + { + mLog.error("Couldn't parse timestamp and bit rate from filename [" + filename + "]"); + } + } + + /** + * Sets the expected bit rate (ie bits per second) for the tracker + */ + public void setBitRate(int bitRate) + { + mBitRate = (double)bitRate; + } + + /** + * Sets the timestamp for the tracker, otherwise current system time is assumed. + */ + public void setTimestamp(long timestamp) + { + mTimestamp = timestamp; + } + + /** + * Gets the currently tracked timestamp when bits per second has been previously specified, otherwise + * returns current system time. + * + * @return current tracked timestamp or current system time. + */ + public long getTimestamp() + { + if(mBitRate != null) + { + return mTimestamp; + } + + return System.currentTimeMillis(); + } + + /** + * Updates the timestamp based on the number of bytes processed and the specified bit rate. + * + * @param bytesProcessed thus far + */ + public void updateBytesProcessed(int bytesProcessed) + { + if(mBitRate != null) + { + mTimestamp += (long)(((double)bytesProcessed * 8.0) / mBitRate * 1000.0); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java b/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java index 861b7e3c..ede4bddf 100644 --- a/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java +++ b/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java @@ -1,22 +1,27 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.record.binary; import io.github.dsheirer.dsp.filter.channelizer.ContinuousReusableBufferProcessor; import io.github.dsheirer.module.Module; +import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.IReusableByteBufferListener; import io.github.dsheirer.sample.buffer.ReusableByteBuffer; @@ -56,18 +61,21 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene private String mRecordingIdentifier; private BinaryWriter mBinaryWriter = new BinaryWriter(); private int mBytesRecordedCounter; + private Protocol mProtocol; /** * Constructs a binary recorder. * * @param baseRecordingPath where the recording should be created * @param recordingIdentifier to include in the recording file name. + * @param protocol to include as a values in the recording file name */ - public BinaryRecorder(Path baseRecordingPath, String recordingIdentifier) + public BinaryRecorder(Path baseRecordingPath, String recordingIdentifier, Protocol protocol) { mBaseRecordingPath = baseRecordingPath; mRecordingIdentifier = recordingIdentifier; mBufferProcessor.setListener(mBinaryWriter); + mProtocol = protocol; } public void start() @@ -118,6 +126,8 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene StringBuilder sb = new StringBuilder(); sb.append(TimeStamp.getTimeStamp("_")); sb.append("_"); + sb.append(mProtocol.getBitRate()).append("BPS_"); + sb.append(mProtocol.getFileNameLabel()).append("_"); sb.append(mRecordingIdentifier.trim()); sb.append(".bits"); String cleaned = StringUtils.replaceIllegalCharacters(sb.toString()); @@ -156,7 +166,7 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene mCurrentPath = path; mWritableByteChannel = Files.newByteChannel(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE)); - mLog.info("Binary (bitstream) recording started: " + mCurrentPath.toString()); +// mLog.info("Binary (bitstream) recording started: " + mCurrentPath.toString()); } } diff --git a/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java b/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java index e810c1a1..e41a2041 100644 --- a/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java +++ b/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java @@ -17,21 +17,19 @@ package io.github.dsheirer.record.wave; import io.github.dsheirer.audio.AudioFormats; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.module.Module; import io.github.dsheirer.sample.ConversionUtils; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; import io.github.dsheirer.util.ThreadPool; -import io.github.dsheirer.util.TimeStamp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sound.sampled.AudioFormat; import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledFuture; @@ -46,32 +44,38 @@ public class AudioPacketWaveRecorder extends Module implements Listener mTransferQueue = new OverflowableReusableBufferTransferQueue<>(500, 100); private BufferProcessor mBufferProcessor; private ScheduledFuture mProcessorHandle; - private Metadata mMetadata; private long mLastBufferReceived; private List mAudioPacketsToProcess = new ArrayList<>(); - + private IdentifierCollection mIdentifierCollection; private AtomicBoolean mRunning = new AtomicBoolean(); /** * Wave audio recorder for AudioPackets - * @param filePrefix - * @param metadata + * + * @param path for the recording file */ - public AudioPacketWaveRecorder(String filePrefix, Metadata metadata) + public AudioPacketWaveRecorder(Path path) { - mMetadata = metadata; - mFilePrefix = filePrefix; + mPath = path; mAudioFormat = AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO; } + /** + * Identifier collection harvested from the most recent audio packet. + * @return identifier collection or null. + */ + public IdentifierCollection getIdentifierCollection() + { + return mIdentifierCollection; + } + /** * Indicates if the recorder is currently running. */ @@ -88,9 +92,9 @@ public class AudioPacketWaveRecorder extends Module implements Listener patchedAliases = patchGroupAlias.getPatchedAliases(); - - if(patchedAliases.size() >= 1) - { - Alias patch = patchedAliases.get(0); - waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_1, patch.getName()); - } - if(patchedAliases.size() >= 2) - { - Alias patch = patchedAliases.get(1); - waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_2, patch.getName()); - } - if(patchedAliases.size() >= 3) - { - Alias patch = patchedAliases.get(2); - waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_3, patch.getName()); - } - if(patchedAliases.size() >= 4) - { - Alias patch = patchedAliases.get(3); - waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_4, patch.getName()); - } - if(patchedAliases.size() >= 5) - { - Alias patch = patchedAliases.get(4); - waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_5, patch.getName()); - } - } - } - } - - AliasedIdentifier secondaryFrom = audioMetadata.getSecondaryAddressFrom(); - - if(secondaryFrom != null) - { - waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_FROM, secondaryFrom.getIdentifier()); - - Alias alias = secondaryFrom.getAlias(); - - if(alias != null) - { - waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_FROM_ALIAS, alias.getName()); - waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_FROM_ICON, alias.getIconName()); - } - } - - AliasedIdentifier secondaryTo = audioMetadata.getSecondaryAddressTo(); - - if(secondaryTo != null) - { - waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_TO, secondaryTo.getIdentifier()); - - Alias alias = secondaryTo.getAlias(); - - if(alias != null) - { - waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_TO_ALIAS, alias.getName()); - waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_TO_ICON, alias.getIconName()); - } - } - - return waveMetadata; + return null; } +// /** +// * Creates a WAVE recording metadata chunk from the audio metadata argument +// * @param audioMetadata to create the wave metadata from +// * @return wave metadata instance +// */ +// public static WaveMetadata createFrom(Metadata audioMetadata) +// { +// WaveMetadata waveMetadata = new WaveMetadata(); +// +// waveMetadata.add(WaveMetadataType.SOFTWARE, SystemProperties.getInstance().getApplicationName()); +// waveMetadata.add(WaveMetadataType.DATE_CREATED, SDF.format(new Date(audioMetadata.getTimestamp()))); +// waveMetadata.add(WaveMetadataType.ARTIST_NAME, audioMetadata.getChannelConfigurationSystem()); +// waveMetadata.add(WaveMetadataType.ALBUM_TITLE, audioMetadata.getChannelConfigurationSite()); +// waveMetadata.add(WaveMetadataType.TRACK_TITLE, audioMetadata.getChannelConfigurationName()); +// waveMetadata.add(WaveMetadataType.COMMENTS, audioMetadata.getPrimaryDecoderType().getDisplayString()); +// waveMetadata.add(WaveMetadataType.CHANNEL_ID, audioMetadata.getChannelFrequencyLabel()); +// waveMetadata.add(WaveMetadataType.CHANNEL_FREQUENCY, String.valueOf(audioMetadata.getChannelFrequency())); +// +// AliasedIdentifier networkID1 = audioMetadata.getNetworkID1(); +// +// if(networkID1 != null) +// { +// waveMetadata.add(WaveMetadataType.NETWORK_ID_1, networkID1.getIdentifier()); +// } +// +// AliasedIdentifier networkID2 = audioMetadata.getNetworkID2(); +// +// if(networkID2 != null) +// { +// waveMetadata.add(WaveMetadataType.NETWORK_ID_2, networkID2.getIdentifier()); +// } +// +// AliasedIdentifier primaryFrom = audioMetadata.getPrimaryAddressFrom(); +// +// if(primaryFrom != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_FROM, primaryFrom.getIdentifier()); +// +// Alias alias = primaryFrom.getAlias(); +// +// if(alias != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_FROM_ALIAS, alias.getName()); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_FROM_ICON, alias.getIconName()); +// } +// } +// +// AliasedIdentifier primaryTo = audioMetadata.getPrimaryAddressTo(); +// +// if(primaryTo != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_TO, primaryTo.getIdentifier()); +// +// Alias alias = primaryTo.getAlias(); +// +// if(alias != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_TO_ALIAS, alias.getName()); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_TO_ICON, alias.getIconName()); +// +// if(alias instanceof PatchGroupAlias) +// { +// PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias; +// +// List patchedAliases = patchGroupAlias.getPatchedAliases(); +// +// if(patchedAliases.size() >= 1) +// { +// Alias patch = patchedAliases.get(0); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_1, patch.getName()); +// } +// if(patchedAliases.size() >= 2) +// { +// Alias patch = patchedAliases.get(1); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_2, patch.getName()); +// } +// if(patchedAliases.size() >= 3) +// { +// Alias patch = patchedAliases.get(2); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_3, patch.getName()); +// } +// if(patchedAliases.size() >= 4) +// { +// Alias patch = patchedAliases.get(3); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_4, patch.getName()); +// } +// if(patchedAliases.size() >= 5) +// { +// Alias patch = patchedAliases.get(4); +// waveMetadata.add(WaveMetadataType.TALKGROUP_PRIMARY_PATCHED_5, patch.getName()); +// } +// } +// } +// } +// +// AliasedIdentifier secondaryFrom = audioMetadata.getSecondaryAddressFrom(); +// +// if(secondaryFrom != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_FROM, secondaryFrom.getIdentifier()); +// +// Alias alias = secondaryFrom.getAlias(); +// +// if(alias != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_FROM_ALIAS, alias.getName()); +// waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_FROM_ICON, alias.getIconName()); +// } +// } +// +// AliasedIdentifier secondaryTo = audioMetadata.getSecondaryAddressTo(); +// +// if(secondaryTo != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_TO, secondaryTo.getIdentifier()); +// +// Alias alias = secondaryTo.getAlias(); +// +// if(alias != null) +// { +// waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_TO_ALIAS, alias.getName()); +// waveMetadata.add(WaveMetadataType.TALKGROUP_SECONDARY_TO_ICON, alias.getIconName()); +// } +// } +// +// return waveMetadata; +// } + /** * Converts the integer length to an ID3 compatible length field where each byte only uses the 7 least significant * bits for a maximum of 28 bits used out of the 32-bit representation. diff --git a/src/main/java/io/github/dsheirer/record/wave/WaveWriter.java b/src/main/java/io/github/dsheirer/record/wave/WaveWriter.java index d6901da9..826af177 100644 --- a/src/main/java/io/github/dsheirer/record/wave/WaveWriter.java +++ b/src/main/java/io/github/dsheirer/record/wave/WaveWriter.java @@ -19,6 +19,8 @@ package io.github.dsheirer.record.wave; import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.sound.sampled.AudioFormat; import java.io.IOException; @@ -35,6 +37,8 @@ import java.util.regex.Pattern; public class WaveWriter implements AutoCloseable { + private final static Logger mLog = LoggerFactory.getLogger(WaveWriter.class); + public static final String RIFF_ID = "RIFF"; public static final int INITIAL_TOTAL_LENGTH = 4; public static final String WAVE_ID = "WAVE"; @@ -127,40 +131,43 @@ public class WaveWriter implements AutoCloseable } /** - * Closes the file + * Implements the auto-closeable interface. Closes the recording without any renaming operation. + * @throws IOException if there is an error closing the file channel. */ public void close() throws IOException + { + close(null); + } + + /** + * Closes the file and renames/moves the contents to the specified path + */ + public void close(Path path) throws IOException { mFileChannel.force(true); mFileChannel.close(); - rename(); + rename(path); } /** - * Renames the file from *.tmp to *.wav after file has been closed. + * Renames the file to the specified filename moving it to the from *.tmp to *.wav after file has been closed. + * * @throws IOException */ - private void rename() throws IOException + private void rename(Path path) throws IOException { - if(mFile != null && Files.exists(mFile)) + if(mFile != null && Files.exists(mFile) && path != null) { - String renamed = mFile.getFileName().toString(); - renamed = renamed.replace(".tmp", ".wav"); - Path renamedPath = mFile.resolveSibling(renamed); - - int suffix = 1; - - while(Files.exists(renamedPath) && suffix < 4) + if(Files.exists(path)) { - String renamedWithSuffix = mFile.getFileName().toString(); - renamedWithSuffix = renamedWithSuffix.replace(".tmp", "_" + suffix + ".wav"); - Path renamedWithSuffixPath = mFile.resolveSibling(renamedWithSuffix); - renamedPath = mFile.resolveSibling(renamedWithSuffixPath); - suffix++; + mLog.warn("Duplicate recording file detected - ignoring [" + path + "]"); + Files.delete(mFile); + } + else + { + Files.move(mFile, path); } - - Files.move(mFile, renamedPath); } } diff --git a/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBuffer.java b/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBuffer.java index c7085f3d..0a8a5ce5 100644 --- a/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBuffer.java +++ b/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBuffer.java @@ -115,6 +115,7 @@ public abstract class AbstractReusableBuffer if(mUserCount.get() < 0) { + mUserCount.set(0); throw new IllegalStateException("User count is below zero. This indicates that this buffer's decrement" + " user count was invoked by more than the expected user count"); } diff --git a/src/main/java/io/github/dsheirer/sample/buffer/ReusableAudioPacket.java b/src/main/java/io/github/dsheirer/sample/buffer/ReusableAudioPacket.java index c64e6f98..c9daf6a0 100644 --- a/src/main/java/io/github/dsheirer/sample/buffer/ReusableAudioPacket.java +++ b/src/main/java/io/github/dsheirer/sample/buffer/ReusableAudioPacket.java @@ -15,7 +15,13 @@ ******************************************************************************/ package io.github.dsheirer.sample.buffer; -import io.github.dsheirer.channel.metadata.Metadata; +import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; +import io.github.dsheirer.alias.id.priority.Priority; +import io.github.dsheirer.identifier.IdentifierCollection; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * Reusable audio packet that carries audio sample data, associated metadata and a type to indicate if this @@ -24,9 +30,14 @@ import io.github.dsheirer.channel.metadata.Metadata; public class ReusableAudioPacket extends AbstractReusableBuffer { private Type mType = Type.AUDIO; - private Metadata mMetadata; private float[] mAudioSamples; + private IdentifierCollection mIdentifierCollection; + private int mChannelId = 0; + private int mMonitoringPriority = Priority.DEFAULT_PRIORITY; + private boolean mRecordable = false; + private List mBroadcastChannels = new ArrayList<>(); + /** * Constructs a reusable audio packet. This constructor is package private and should only be used by * a reusable audio packet queue. @@ -41,27 +52,137 @@ public class ReusableAudioPacket extends AbstractReusableBuffer } /** - * Indicates if this packet has associated metadata + * Resets the attributes for this packet to default audio priority, non-recordable and non-streamable. */ - public boolean hasMetadata() + public void resetAttributes() { - return mMetadata != null; + mChannelId = 0; + mMonitoringPriority = Priority.DEFAULT_PRIORITY; + mRecordable = false; + mBroadcastChannels.clear(); } /** - * Associated audio metadata + * Identifier for the channel that produced this audio packet. This ID is used to identify all packets + * within a packet stream that come from the same channel. + * + * @return channel identifier */ - public Metadata getMetadata() + public int getAudioChannelId() { - return mMetadata; + return mChannelId; + } + + /** + * Sets the channel identifier for this audio packet + * + * @param channelId for this audio packet. + */ + public void setAudioChannelId(int channelId) + { + mChannelId = channelId; + } + + /** + * Monitoring (ie local playback) priority. + */ + public int getMonitoringPriority() + { + return mMonitoringPriority; + } + + /** + * Sets the monitoring priority + * + * @param priority for monitoring: + * + * -1 Do Not Monitor + * 0 Selected (monitor override) + * 1 Highest + * ... + * 100 Lowest + */ + public void setMonitoringPriority(int priority) + { + mMonitoringPriority = priority; + } + + /** + * Indicates if the monitoring priority of this audio packet is set to do not monitor. + */ + public boolean isDoNotMonitor() + { + return mMonitoringPriority == Priority.DO_NOT_MONITOR; + } + + /** + * Indicates if this audio packet should be recorded. + */ + public boolean isRecordable() + { + return mRecordable; + } + + /** + * Sets the recordable status for this audio packet. + */ + public void setRecordable(boolean recordable) + { + mRecordable = recordable; + } + + /** + * List of broadcast/streaming channels for this audio packet. + */ + public List getBroadcastChannels() + { + return mBroadcastChannels; + } + + /** + * Adds the broadcast channels to this metadata + */ + public void addBroadcastChannels(Collection channels) + { + for(BroadcastChannel channel: channels) + { + if(!mBroadcastChannels.contains(channel)) + { + mBroadcastChannels.add(channel); + } + } + } + + /** + * Indicates if this audio packet has one or more streaming/broadcast channels assigned. + */ + public boolean isStreamable() + { + return !mBroadcastChannels.isEmpty(); + } + + /** + * Indicates if this packet has associated identifier collection + */ + public boolean hasIdentifierCollection() + { + return mIdentifierCollection != null; + } + + /** + * Associated audio metadata and identifiers + */ + public IdentifierCollection getIdentifierCollection() + { + return mIdentifierCollection; } /** * Sets the metadata associated with this audio packet. */ - public void setMetadata(Metadata metadata) + public void setIdentifierCollection(IdentifierCollection identifierCollection) { - mMetadata = metadata; + mIdentifierCollection = identifierCollection; } /** diff --git a/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java b/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java index fc4a8c99..68d434d5 100644 --- a/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java +++ b/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.sample.buffer; import io.github.dsheirer.dsp.filter.channelizer.SampleTimestampManager; @@ -27,6 +31,7 @@ import java.nio.FloatBuffer; */ public class ReusableComplexBufferAssembler { +// private final static Logger mLog = LoggerFactory.getLogger(ReusableComplexBufferAssembler.class); private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("ReusableComplexBufferAssembler"); private SampleTimestampManager mTimestampManager; private FloatBuffer mBuffer; diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java index 253fa784..c67a2340 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java @@ -79,6 +79,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor private JCheckBox mLNAAGC; private JCheckBox mMixerAGC; + private FrequencyCorrectionChangeListener mFrequencyCorrectionChangeListener = new FrequencyCorrectionChangeListener(); private AirspyTunerController mController; private boolean mLoading; @@ -194,26 +195,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor format.setMinimumFractionDigits(1); editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - mFrequencyCorrection.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection(value); - } - catch(SourceException e1) - { - mLog.error("Error setting frequency correction value", e1); - } - - save(); - } - }); + mFrequencyCorrection.addChangeListener(mFrequencyCorrectionChangeListener); add(new JLabel("PPM:")); add(mFrequencyCorrection); @@ -713,7 +695,9 @@ public class AirspyTunerEditor extends TunerConfigurationEditor //Update enabled state to reflect when frequency and sample rate controls are locked mSampleRateCombo.setEnabled(!mController.isLocked()); + mFrequencyCorrection.removeChangeListener(mFrequencyCorrectionChangeListener); mFrequencyCorrection.setValue(airspy.getFrequencyCorrection()); + mFrequencyCorrection.addChangeListener(mFrequencyCorrectionChangeListener); GainMode mode = Gain.getGainMode(airspy.getGain()); @@ -793,4 +777,26 @@ public class AirspyTunerEditor extends TunerConfigurationEditor return sb.toString(); } + + private class FrequencyCorrectionChangeListener implements ChangeListener + { + + @Override + public void stateChanged(ChangeEvent e) + { + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection(value); + } + catch(SourceException e1) + { + mLog.error("Error setting frequency correction value", e1); + } + + save(); + } + } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java index 94ee1924..11c90384 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.source.tuner.usb; import io.github.dsheirer.sample.Listener; @@ -29,6 +33,8 @@ import org.usb4java.Transfer; import org.usb4java.TransferCallback; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -42,13 +48,12 @@ public class USBTransferProcessor implements TransferCallback private static final long USB_TIMEOUT_MS = 2000l; //milliseconds //Number of native byte buffers to allocate for transferring data from the USB device - private static final int TRANSFER_BUFFER_POOL_SIZE = 20; - private static final int MAX_TRANSFERS_IN_PROGRESS_SIZE = 10; - private int mTransfersToSubmit = 0; + private static final int TRANSFER_BUFFER_POOL_SIZE = 40; private LinkedTransferQueue mAvailableTransfers = new LinkedTransferQueue<>(); private LinkedTransferQueue mInProgressTransfers = new LinkedTransferQueue<>(); private LinkedTransferQueue mCompletedTransfers = new LinkedTransferQueue<>(); + private List mTransfersToSubmit = new ArrayList<>(); //Tuner format-specific byte buffer to IQ float sample converter private NativeBufferConverter mNativeBufferConverter; @@ -67,6 +72,8 @@ public class USBTransferProcessor implements TransferCallback private ScheduledFuture mRestartFuture; private String mDeviceName; + private int mTransferErrorLoggingCount = 0; + /** * Manages stream of USB transfer buffers and converts buffers to complex buffer samples for distribution to @@ -119,9 +126,9 @@ public class USBTransferProcessor implements TransferCallback { if(mRunning.compareAndSet(false, true)) { + prepareDeviceStart(); prepareTransfers(); - mTransfersToSubmit = MAX_TRANSFERS_IN_PROGRESS_SIZE; submitTransfers(); //Start transferred buffer dispatcher @@ -138,6 +145,7 @@ public class USBTransferProcessor implements TransferCallback */ private void stop() { + mLog.debug("stop() was invoked"); if(mRunning.compareAndSet(true, false)) { mBufferDispatcherFuture.cancel(true); @@ -161,15 +169,6 @@ public class USBTransferProcessor implements TransferCallback //Unregister from LibUSB processor so that it auto-stops LibUSB processing TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(this); - //Directly invoke the timeout handler to ensure that our cancelled transfer buffers are flushed. - int result = LibUsb.handleEventsTimeoutCompleted(null, USB_TIMEOUT_MS, - mLibUsbHandlerStatus.asIntBuffer()); - - if(result != LibUsb.SUCCESS) - { - mLog.error(mDeviceName + " - error while cancelling transfer buffers during shutdown/pause - error code:" + result); - } - mLibUsbHandlerStatus.rewind(); executeDeviceStop(); @@ -182,8 +181,10 @@ public class USBTransferProcessor implements TransferCallback */ private void restart() { + mLog.debug("Stopping for a restart ...."); stop(); + mLog.debug("Attempting to clear halt condition ..."); //Attempt to clear any halt condition LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); @@ -215,55 +216,50 @@ public class USBTransferProcessor implements TransferCallback { if(mRunning.get()) { - int submitted = 0; + mAvailableTransfers.drainTo(mTransfersToSubmit); - for(int x = 0; x < mTransfersToSubmit; x++) + for(Transfer transfer: mTransfersToSubmit) { - Transfer transfer = mAvailableTransfers.poll(); + int result = LibUsb.submitTransfer(transfer); - if(transfer != null) + if(result == LibUsb.SUCCESS) { - int result = LibUsb.submitTransfer(transfer); + mInProgressTransfers.add(transfer); + } + else if(result == LibUsb.ERROR_PIPE) + { + mLog.warn("USB pipe error - attempting to clear halt on USB device [" + mDeviceName + "]"); - if(result == LibUsb.SUCCESS) + int resetResult = LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); + + if(resetResult == LibUsb.SUCCESS) { - mInProgressTransfers.add(transfer); - submitted++; - } - else if(result == LibUsb.ERROR_PIPE) - { - mLog.warn("USB pipe error - attempting to clear halt on USB device [" + mDeviceName + "]"); + int resubmitResult = LibUsb.submitTransfer(transfer); - int resetResult = LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); - - if(resetResult == LibUsb.SUCCESS) + if(resubmitResult == LibUsb.SUCCESS) { - int resubmitResult = LibUsb.submitTransfer(transfer); - - if(resubmitResult == LibUsb.SUCCESS) - { - mInProgressTransfers.add(transfer); - submitted++; - } - else - { - mLog.error(mDeviceName + " - error resubmitting transfer after endpoint clear halt"); - } + mInProgressTransfers.add(transfer); } else { - mLog.error(mDeviceName + " - unable to clear device endpoint halt"); + mAvailableTransfers.add(transfer); + mLog.error(mDeviceName + " - error resubmitting transfer after endpoint clear halt"); } } else { - //TODO: broadcast to each listener that this source has an error and is shutting down - mLog.error(mDeviceName + "- error submitting transfer [" + LibUsb.errorName(result) + "]"); + mAvailableTransfers.add(transfer); + mLog.error(mDeviceName + " - unable to clear device endpoint halt"); } } + else + { + mAvailableTransfers.add(transfer); + mLog.error(mDeviceName + "- error submitting transfer [" + LibUsb.errorName(result) + "]"); + } } - mTransfersToSubmit -= submitted; + mTransfersToSubmit.clear(); } } @@ -355,6 +351,31 @@ public class USBTransferProcessor implements TransferCallback mAvailableTransfers.add(transfer); } break; + case LibUsb.TRANSFER_ERROR: + if(transfer.actualLength() > 0) + { + mCompletedTransfers.add(transfer); + } + else + { + transfer.buffer().rewind(); + mAvailableTransfers.add(transfer); + } + + mTransferErrorLoggingCount++; + + if(mTransferErrorLoggingCount <= 5) + { + //TODO: this error situation should be raised/highlighted to the user for action in the gui ... + mLog.warn("USB buffer transfer error detected. You should try using a different USB port for the usb-based tuner"); + + //Handle integer overflow + if(mTransferErrorLoggingCount < 0) + { + mTransferErrorLoggingCount = 1; + } + } + break; case LibUsb.TRANSFER_CANCELLED: transfer.buffer().rewind(); mAvailableTransfers.add(transfer); @@ -368,11 +389,6 @@ public class USBTransferProcessor implements TransferCallback restart(); return; } - - mTransfersToSubmit++; - - //Dispatch a new transfer - submitTransfers(); } /** @@ -432,6 +448,8 @@ public class USBTransferProcessor implements TransferCallback transfer.buffer().rewind(); mAvailableTransfers.add(transfer); + submitTransfers(); + //Fetch the next transfer to process transfer = mCompletedTransfers.poll(); } diff --git a/src/main/java/io/github/dsheirer/spectrum/OverflowableBufferStream.java b/src/main/java/io/github/dsheirer/spectrum/OverflowableBufferStream.java index af6077d2..46c90006 100644 --- a/src/main/java/io/github/dsheirer/spectrum/OverflowableBufferStream.java +++ b/src/main/java/io/github/dsheirer/spectrum/OverflowableBufferStream.java @@ -209,7 +209,6 @@ public class OverflowableBufferStream extends OverflowableTransferQueue - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.spectrum; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.Channel.ChannelType; import io.github.dsheirer.controller.channel.ChannelEvent; -import io.github.dsheirer.controller.channel.ChannelEventListener; import io.github.dsheirer.controller.channel.ChannelModel; +import io.github.dsheirer.controller.channel.ChannelProcessingManager; +import io.github.dsheirer.sample.Listener; import io.github.dsheirer.settings.ColorSetting; import io.github.dsheirer.settings.ColorSetting.ColorSettingName; import io.github.dsheirer.settings.Setting; @@ -31,8 +33,6 @@ import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.channel.TunerChannel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.swing.JPanel; import java.awt.BasicStroke; @@ -50,14 +50,14 @@ import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -public class OverlayPanel extends JPanel implements ChannelEventListener, ISourceEventProcessor, - SettingChangeListener +public class OverlayPanel extends JPanel implements Listener, ISourceEventProcessor, SettingChangeListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(OverlayPanel.class); +// private final static Logger mLog = LoggerFactory.getLogger(OverlayPanel.class); private final DecimalFormat PPM_FORMATTER = new DecimalFormat( "#.0" ); private final static RenderingHints RENDERING_HINTS = new RenderingHints(RenderingHints.KEY_ANTIALIASING, @@ -92,7 +92,9 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, ISourc private Color mColorSpectrumLine; //Currently visible/displayable channels - private CopyOnWriteArrayList mVisibleChannels = new CopyOnWriteArrayList(); + private List mVisibleChannels = new CopyOnWriteArrayList<>(); + private List mTrafficChannels = new CopyOnWriteArrayList<>(); + private ChannelDisplay mChannelDisplay = ChannelDisplay.ALL; //Defines the offset at the bottom of the spectral display to account for @@ -102,13 +104,14 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, ISourc private SettingsManager mSettingsManager; private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; /** * Translucent overlay panel for displaying channel configurations, * processing channels, selected channels, frequency labels and lines, and * a cursor with a frequency readout. */ - public OverlayPanel(SettingsManager settingsManager, ChannelModel channelModel) + public OverlayPanel(SettingsManager settingsManager, ChannelModel channelModel, ChannelProcessingManager channelProcessingManager) { mSettingsManager = settingsManager; @@ -121,7 +124,14 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, ISourc if(mChannelModel != null) { - mChannelModel.addListener(this); + mChannelModel.addListener(this::receive); + } + + mChannelProcessingManager = channelProcessingManager; + + if(mChannelProcessingManager != null) + { + mChannelProcessingManager.addChannelEventListener(this::receive); } addComponentListener(mLabelSizeMonitor); @@ -668,14 +678,21 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, ISourc */ mVisibleChannels.clear(); mVisibleChannels.addAll(mChannelModel.getChannelsInFrequencyRange(getMinFrequency(), getMaxFrequency())); + + for(Channel trafficChannel: mTrafficChannels) + { + if(trafficChannel.isWithin(getMinFrequency(), getMaxFrequency())) + { + mVisibleChannels.add(trafficChannel); + } + } } /** * Channel change event handler */ @Override - @SuppressWarnings("incomplete-switch") - public void channelChanged(ChannelEvent event) + public void receive(ChannelEvent event) { Channel channel = event.getChannel(); @@ -683,6 +700,10 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, ISourc { case NOTIFICATION_ADD: case NOTIFICATION_PROCESSING_START: + if(channel.getChannelType() == ChannelType.TRAFFIC && !mTrafficChannels.contains(channel)) + { + mTrafficChannels.add(channel); + } if(!mVisibleChannels.contains(channel) && channel.isWithin(getMinFrequency(), getMaxFrequency())) { mVisibleChannels.add(channel); @@ -692,9 +713,11 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, ISourc mVisibleChannels.remove(channel); break; case NOTIFICATION_PROCESSING_STOP: + case NOTIFICATION_PROCESSING_START_REJECTED: if(channel.getChannelType() == ChannelType.TRAFFIC) { mVisibleChannels.remove(channel); + mTrafficChannels.remove(channel); } break; case NOTIFICATION_CONFIGURATION_CHANGE: diff --git a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java index c6888e31..963db996 100644 --- a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java @@ -126,7 +126,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener - ******************************************************************************/ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ package io.github.dsheirer.util; import java.text.SimpleDateFormat; @@ -22,97 +24,99 @@ import java.util.Date; public class TimeStamp { - private static SimpleDateFormat mSDFDate = new SimpleDateFormat( "yyyyMMdd" ); - private static SimpleDateFormat mSDFTime = new SimpleDateFormat( "HHmmss" ); - private static SimpleDateFormat mSDFTimeWithMilliseconds = new SimpleDateFormat( "HHmmss.SSS" ); + public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + public static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HHmmss"); + public static SimpleDateFormat DATE_WITH_MILLISECONDS_FORMAT = new SimpleDateFormat("HHmmss.SSS"); + public static SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); + public static SimpleDateFormat DATE_TIME_MILLIS_FORMAT = new SimpleDateFormat("yyyyMMdd HHmmss.SSS"); - /** - * Returns the current system date formatted as yyyy-MM-dd - */ - public static String getFormattedDate() - { - return getFormattedDate( System.currentTimeMillis() ); - } + /** + * Returns the current system date formatted as yyyy-MM-dd + */ + public static synchronized String getFormattedDate() + { + return getFormattedDate(System.currentTimeMillis()); + } - /** - * Returns the timestamp formatted as a date of yyyy-MM-dd - */ - public static String getFormattedDate( long timestamp ) - { - return mSDFDate.format( new Date( timestamp ) ); - } - - /** - * Returns the current system time formatted as HH:mm:ss - */ - public static String getFormattedTime() - { - return getFormattedTime( System.currentTimeMillis() ); - } + /** + * Returns the timestamp formatted as a date of yyyy-MM-dd + */ + public static synchronized String getFormattedDate(long timestamp) + { + return DATE_FORMAT.format(new Date(timestamp)); + } - /** - * Returns the timestamp formatted as a time of HH:mm:ss - */ - public static String getFormattedTime( long timestamp ) - { - return mSDFTime.format( new Date( timestamp ) ); - } + /** + * Returns the current system time formatted as HH:mm:ss + */ + public static synchronized String getFormattedTime() + { + return getFormattedTime(System.currentTimeMillis()); + } - /** - * Returns the timestamp formatted as a time of HH:mm:ss - */ - public static String getFormattedTimeWithMilliseconds( long timestamp ) - { - return mSDFTimeWithMilliseconds.format( new Date( timestamp ) ); - } + /** + * Returns the timestamp formatted as a time of HH:mm:ss + */ + public static synchronized String getFormattedTime(long timestamp) + { + return TIME_FORMAT.format(new Date(timestamp)); + } - /** - * Returns current system time formatted as yyyy-MM-dd*HH:mm:ss - * with the * representing the separator attribute - */ - public static String getTimeStamp( String separator ) - { - return getTimeStamp( System.currentTimeMillis(), separator ); - } - - /** - * Returns timestamp formatted as yyyy-MM-dd*HH:mm:ss - * with the * representing the separator attribute - */ - public static String getTimeStamp( long timestamp, String separator ) - { - StringBuilder sb = new StringBuilder(); - - sb.append( getFormattedDate( timestamp ) ); - sb.append( separator ); - sb.append( getFormattedTime( timestamp ) ); + /** + * Returns the timestamp formatted as a time of HH:mm:ss + */ + public static synchronized String getFormattedTimeWithMilliseconds(long timestamp) + { + return DATE_WITH_MILLISECONDS_FORMAT.format(new Date(timestamp)); + } - return sb.toString(); - } - - /** - * Returns current system time formatted as yyyy-MM-dd*HH:mm:ss.SSS - * with the * representing the separator attribute - */ - public static String getLongTimeStamp( String separator ) - { - return getLongTimeStamp( System.currentTimeMillis(), separator ); - } - - /** - * Returns timestamp formatted as yyyy-MM-dd*HH:mm:ss.SSS - * with the * representing the separator attribute - */ - public static String getLongTimeStamp( long timestamp, String separator ) - { - StringBuilder sb = new StringBuilder(); - - sb.append( getFormattedDate( timestamp ) ); - sb.append( separator ); - sb.append( getFormattedTimeWithMilliseconds( timestamp ) ); + /** + * Returns current system time formatted as yyyy-MM-dd*HH:mm:ss + * with the * representing the separator attribute + */ + public static synchronized String getTimeStamp(String separator) + { + return getTimeStamp(System.currentTimeMillis(), separator); + } + + /** + * Returns timestamp formatted as yyyy-MM-dd*HH:mm:ss + * with the * representing the separator attribute + */ + public static synchronized String getTimeStamp(long timestamp, String separator) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getFormattedDate(timestamp)); + sb.append(separator); + sb.append(getFormattedTime(timestamp)); + + return sb.toString(); + } + + /** + * Returns current system time formatted as yyyy-MM-dd*HH:mm:ss.SSS + * with the * representing the separator attribute + */ + public static synchronized String getLongTimeStamp(String separator) + { + return getLongTimeStamp(System.currentTimeMillis(), separator); + } + + /** + * Returns timestamp formatted as yyyy-MM-dd*HH:mm:ss.SSS + * with the * representing the separator attribute + */ + public static synchronized String getLongTimeStamp(long timestamp, String separator) + { + StringBuilder sb = new StringBuilder(); + + sb.append(getFormattedDate(timestamp)); + sb.append(separator); + sb.append(getFormattedTimeWithMilliseconds(timestamp)); + + return sb.toString(); + } - return sb.toString(); - } - } diff --git a/src/main/java/org/jdesktop/swingx/mapviewer/GeoPosition.java b/src/main/java/org/jdesktop/swingx/mapviewer/GeoPosition.java index de3b1db4..d85c8c02 100644 --- a/src/main/java/org/jdesktop/swingx/mapviewer/GeoPosition.java +++ b/src/main/java/org/jdesktop/swingx/mapviewer/GeoPosition.java @@ -10,43 +10,51 @@ package org.jdesktop.swingx.mapviewer; /** - * An immutable coordinate in the real (geographic) world, + * An immutable coordinate in the real (geographic) world, * composed of a latitude and a longitude. + * * @author rbair */ -public class GeoPosition { +public class GeoPosition +{ private double latitude; private double longitude; - + /** * Creates a new instance of GeoPosition from the specified * latitude and longitude. These are double values in decimal degrees, not * degrees, minutes, and seconds. Use the other constructor for those. + * * @param latitude a latitude value in decmial degrees * @param longitude a longitude value in decimal degrees */ - public GeoPosition(double latitude, double longitude) { + public GeoPosition(double latitude, double longitude) + { this.latitude = latitude; this.longitude = longitude; } // must be an array of length two containing lat then long in that order. + /** * Creates a new instance of GeoPosition from the specified * latitude and longitude as an array of two doubles, with the * latitude first. These are double values in decimal degrees, not * degrees, minutes, and seconds. Use the other constructor for those. + * * @param coords latitude and longitude as a double array of length two */ - public GeoPosition(double [] coords) { + public GeoPosition(double[] coords) + { this.latitude = coords[0]; this.longitude = coords[1]; } - + /** * Creates a new instance of GeoPosition from the specified - * latitude and longitude. + * latitude and longitude. * Each are specified as degrees, minutes, and seconds; not * as decimal degrees. Use the other constructor for those. + * * @param latDegrees the degrees part of the current latitude * @param latMinutes the minutes part of the current latitude * @param latSeconds the seconds part of the current latitude @@ -55,59 +63,80 @@ public class GeoPosition { * @param lonSeconds the seconds part of the current longitude */ public GeoPosition(int latDegrees, int latMinutes, int latSeconds, - int lonDegrees, int lonMinutes, int lonSeconds) { - this(latDegrees + (latMinutes + latSeconds/60.0)/60.0, - lonDegrees + (lonMinutes + lonSeconds/60.0)/60.0); + int lonDegrees, int lonMinutes, int lonSeconds) + { + this(latDegrees + (latMinutes + latSeconds / 60.0) / 60.0, + lonDegrees + (lonMinutes + lonSeconds / 60.0) / 60.0); } - + /** * Get the latitude as decimal degrees + * * @return the latitude as decimal degrees */ - public double getLatitude() { + public double getLatitude() + { return latitude; } - + /** * Get the longitude as decimal degrees + * * @return the longitude as decimal degrees */ - public double getLongitude() { + public double getLongitude() + { return longitude; } - + @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - long temp; - temp = Double.doubleToLongBits(latitude); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(longitude); - result = prime * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof GeoPosition)) - return false; - GeoPosition other = (GeoPosition) obj; - if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) - return false; - if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude)) - return false; - return true; - } - + public int hashCode() + { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int)(temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int)(temp ^ (temp >>> 32)); + return result; + } + @Override - public String toString() { + public boolean equals(Object obj) + { + if(this == obj) + { + return true; + } + if(obj == null) + { + return false; + } + if(!(obj instanceof GeoPosition)) + { + return false; + } + GeoPosition other = (GeoPosition)obj; + if(Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) + { + return false; + } + if(Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude)) + { + return false; + } + return true; + } + + @Override + public String toString() + { return "[" + latitude + ", " + longitude + "]"; } + + public boolean isValid() + { + return -90.0 <= latitude && latitude <= 90.0 && -180.0 <= longitude && longitude <= 180.0; + } } \ No newline at end of file