From f4d8ea281dd55535b1441afea33aff6a330e93cd Mon Sep 17 00:00:00 2001 From: denny Date: Tue, 1 Sep 2015 07:10:26 -0400 Subject: [PATCH] Initial commit of redesigned internal processing and audio management. Note: Several of the decoders are currently non-functioning in this commit. If you want the latest functioning codebase, please checkout the commit prior to this one. All decoders will eventually be updated with this new architecture. Currently broken: - Logging and recording - Aux decoders (Fleetsync/MDC/LoJack/Tait) - LTR/Passport decoders Changes in this commit: - Module architecture for decoders and components with central provisioning by the DecoderFactory - Transition from Complex sample stream processing to float buffer processing for improved memory management - Redesigned filter structures - to support buffers and streams - Resolved several memory leak issues - Universal sharing of the thread pool manager - Added centralized audio management with call/user prioritization and new gui with mute/volume controls - Embedded audio metadata for eventual streaming support - Redesigned ProcessingChain to support independent re-use - Added new TrafficChannelManager to handle traffic channel allocations - Traffic Channel pool size and Call timeout available in P25 and MPT1327 configs - Redesigned channel and decoder gui components --- ...ace-0.2.0.jar => jmbe-interface-0.2.1.jar} | Bin 15493 -> 16219 bytes src/.directory | 4 + src/alias/AliasList.java | 54 + src/alias/Metadata.java | 102 + src/alias/MetadataReset.java | 14 + src/alias/MetadataType.java | 38 + src/alias/action/AliasActionManager.java | 2 +- src/audio/AudioChannelPanel.java | 356 ++ src/audio/AudioEvent.java | 30 + src/audio/AudioException.java | 16 + src/audio/AudioManager.java | 379 ++ src/audio/AudioModule.java | 191 + src/audio/AudioOutputImpl.java | 124 - src/audio/AudioOutputManager.java | 25 - src/audio/AudioPacket.java | 32 +- src/audio/AudioPacketRateCounter.java | 56 + src/audio/AudioPanel.java | 120 + src/audio/IAudioController.java | 36 + src/audio/IAudioOutput.java | 29 - src/audio/IAudioPacketProvider.java | 9 + src/audio/inverted/AudioAdapter.java | 4 +- src/audio/inverted/AudioInverter.java | 6 +- src/audio/metadata/AudioMetadata.java | 180 + src/audio/metadata/IMetadataListener.java | 9 + src/audio/metadata/IMetadataProvider.java | 10 + src/audio/output/AudioOutput.java | 84 + src/audio/{ => output}/MonoAudioOutput.java | 117 +- src/audio/squelch/ISquelchStateListener.java | 8 + src/audio/squelch/ISquelchStateProvider.java | 9 + .../squelch/ManualSquelchController.java | 40 + src/audio/squelch/SquelchController.java | 122 + src/audio/squelch/SquelchMode.java | 25 + src/audio/squelch/SquelchState.java | 7 + .../squelch/TrunkingSquelchController.java} | 64 +- src/buffer/ComplexByteBuffer.java | 8 +- src/buffer/ComplexCircularBuffer.java | 18 +- src/buffer/ComplexSampleBufferAssembler.java | 4 +- src/buffer/ComplexTappedCircularBuffer.java | 12 +- src/buffer/ShortComplexByteBuffer.java | 8 +- src/buffer/StereoByteBuffer.java | 8 +- .../ConfigurationControllerModel.java | 11 +- src/controller/ControllerPanel.java | 27 +- src/controller/NamingThreadFactory.java | 48 + src/controller/ResourceManager.java | 16 +- src/controller/ThreadPoolManager.java | 16 +- src/controller/channel/Channel.java | 440 ++- src/controller/channel/ChannelEditor.java | 6 +- src/controller/channel/ChannelEvent.java | 17 +- src/controller/channel/ChannelManager.java | 11 +- src/controller/channel/ChannelNode.java | 10 +- .../channel/ChannelSelectionListener.java | 4 +- .../channel/IChannelEventListener.java | 8 + .../channel/IChannelEventProvider.java | 9 + .../channel/{ => map}/ChannelMap.java | 2 +- .../channel/{ => map}/ChannelMapEditor.java | 4 +- .../channel/{ => map}/ChannelMapList.java | 2 +- .../channel/{ => map}/ChannelMapListNode.java | 2 +- .../channel/{ => map}/ChannelMapModel.java | 2 +- .../channel/{ => map}/ChannelMapNode.java | 2 +- .../channel/{ => map}/ChannelRange.java | 2 +- src/controller/config/Configuration.java | 4 +- src/controller/state/AuxChannelState.java | 80 - src/controller/state/ChannelState.java | 697 ---- src/controller/state/ChannelStatePanel.java | 388 -- src/decode/DecodeEditorFactory.java | 63 - src/decode/Decoder.java | 276 -- src/decode/DecoderFactory.java | 219 -- src/decode/am/AMChannelState.java | 69 - src/decode/am/AMDecoder.java | 105 - src/decode/am/AMPanel.java | 136 - .../fleetsync2/FleetsyncActivitySummary.java | 171 - .../fleetsync2/FleetsyncChannelState.java | 193 - src/decode/lj1200/LJ1200ChannelState.java | 67 - src/decode/lj1200/LJ1200MessageFilter.java | 36 - src/decode/ltrnet/LTRNetActivitySummary.java | 404 -- src/decode/ltrnet/LTRNetChannelState.java | 433 --- src/decode/ltrnet/LTRNetDecoder.java | 175 - src/decode/ltrnet/LTRNetMessageProcessor.java | 165 - src/decode/ltrstandard/LTRChannelState.java | 240 -- .../LTRStandardActivitySummary.java | 209 - .../ltrstandard/LTRStandardDecoder.java | 141 - src/decode/mdc1200/MDCActivitySummary.java | 131 - src/decode/mdc1200/MDCChannelState.java | 183 - .../mpt1327/MPT1327ActivitySummary.java | 159 - src/decode/mpt1327/MPT1327ChannelState.java | 422 -- src/decode/mpt1327/MPT1327Panel.java | 282 -- src/decode/mpt1327/MPT1327TrafficPanel.java | 258 -- src/decode/nbfm/NBFMChannelState.java | 69 - src/decode/nbfm/NBFMDecoder.java | 99 - src/decode/nbfm/NBFMPanel.java | 136 - src/decode/p25/P25ActivitySummary.java | 350 -- src/decode/p25/P25ChannelState.java | 3408 ----------------- src/decode/p25/P25Decoder.java | 406 -- src/decode/p25/audio/IMBETargetDataLine.java | 273 -- src/decode/p25/audio/P25AudioCodecTest.java | 50 - src/decode/p25/audio/P25AudioOutput.java | 154 - .../passport/PassportActivitySummary.java | 233 -- src/decode/passport/PassportChannelState.java | 369 -- src/decode/passport/PassportDecoder.java | 165 - src/decode/tait/Tait1200MessageFilter.java | 35 - src/dsp/am/AMDemodulator.java | 53 +- .../am/AMDemodulator_CB.java} | 75 +- src/dsp/filter/ComplexCICDecimate.java | 24 +- ...IRFilter.java => ComplexFIRFilterOld.java} | 20 +- src/dsp/filter/ComplexFilter.java | 13 +- src/dsp/filter/ComplexHalfBandFilter.java | 18 +- .../ComplexHalfBandNoDecimateFilter.java | 32 +- .../ComplexPolyphaseDecimationFilter.java | 16 +- src/dsp/filter/FilterFactory.java | 206 +- src/dsp/filter/Filters.java | 2613 +++++++------ src/dsp/filter/FloatFIRFilter.java | 10 +- src/dsp/filter/FloatHalfBandFilter.java | 10 +- .../filter/FloatHalfBandNoDecimateFilter.java | 10 +- .../{ => cic}/ComplexPrimeCICDecimate.java | 103 +- .../dc/AveragingDCRemovalFilter_RB.java} | 58 +- .../filter/dc/DCRemovalFilter.java} | 34 +- .../DCRemovalFilterLyons.java} | 11 +- src/dsp/filter/dc/DCRemovalFilter_RB.java | 31 + .../DCRemovalFilter_R_R.java} | 38 +- .../IIRSinglePoleDCRemovalFilter.java} | 6 +- .../dc/IIRSinglePoleDCRemovalFilter_RB.java | 58 + src/dsp/filter/equalizer/CMAEqualizer.java | 38 +- src/dsp/filter/fir/FIRFilter.java | 6 + .../filter/fir/complex/ComplexFIRFilter.java | 54 + .../fir/complex/ComplexFIRFilter_CB_CB.java | 54 + src/dsp/filter/fir/real/RealFIRFilter.java | 105 + .../filter/fir/real/RealFIRFilter_RB_RB.java | 67 + .../filter/fir/real/RealFIRFilter_R_R.java | 56 + .../complex/HalfBandFilter_CB_CB.java | 117 + .../filter/halfband/real/HalfBandFilter.java | 160 + .../halfband/real/HalfBandFilter_RB_RB.java | 101 + .../filter/interpolator/QPSKInterpolator.java | 61 - .../filter/interpolator/RealInterpolator.java | 58 + src/dsp/fm/FMDemodulator.java | 78 + src/dsp/fm/FMDemodulator_CB.java | 81 + src/dsp/{nbfm => fm}/FMDiscriminator.java | 13 +- .../FilteringNBFMDemodulator.java | 21 +- src/dsp/{nbfm => fm}/NBFMDemodulator.java | 19 +- src/dsp/fsk/FSK2Decoder.java | 31 +- src/dsp/fsk/LTRFSKDecoder.java | 29 +- ...Control.java => AutomaticGainControl.java} | 52 +- src/dsp/gain/AutomaticGainControl_RB.java | 53 + src/dsp/gain/ComplexAutomaticGainControl.java | 16 +- .../gain/ComplexFeedForwardGainControl.java | 26 +- src/dsp/gain/ComplexGain.java | 10 +- src/dsp/psk/CQPSKDemodulator.java | 167 +- src/dsp/psk/CQPSKDemodulator2.java | 87 +- src/dsp/psk/QPSKPolarSlicer.java | 35 +- src/dsp/psk/QPSKStarSlicer.java | 8 +- src/eventlog/CallEventLogger.java | 2 +- src/eventlog/EventLogManager.java | 4 +- src/filter/FilterSet.java | 5 + src/gui/SDRTrunk.java | 3 - src/gui/control/ConstellationViewer.java | 18 +- src/instrument/gui/AudioSourceFrame.java | 39 +- src/instrument/gui/ConstellationPanel.java | 6 +- src/instrument/gui/DecoderViewFrame.java | 15 +- src/instrument/tap/TapType.java | 1 + .../tap/stream/ComplexSampleTap.java | 65 + .../tap/stream/ComplexSampleTapViewPanel.java | 155 + src/instrument/tap/stream/ComplexTap.java | 21 +- .../tap/stream/ComplexTapViewPanel.java | 8 +- src/instrument/tap/stream/EyeDiagramData.java | 20 +- .../tap/stream/EyeDiagramDataTap.java | 3 +- .../tap/stream/EyeDiagramDataTapPanel.java | 21 +- src/instrument/tap/stream/FloatBufferTap.java | 38 + src/instrument/tap/stream/QPSKTapPanel.java | 8 +- src/map/MapService.java | 4 +- src/message/IMessageListener.java | 8 + src/message/IMessageProvider.java | 9 + src/message/Message.java | 2 +- src/message/MessageDirection.java | 16 +- src/message/MessageFilterFactory.java | 111 - src/module/Module.java | 57 + src/module/ProcessingChain.java | 661 ++++ .../ProcessingChainOld.java} | 239 +- .../decode/AuxDecodeComponentEditor.java | 4 +- .../decode/DecodeComponentEditor.java | 17 +- src/{ => module}/decode/DecodeEditor.java | 11 +- src/module/decode/Decoder.java | 143 + src/module/decode/DecoderFactory.java | 416 ++ src/{ => module}/decode/DecoderType.java | 6 +- src/module/decode/PrimaryDecoder.java | 79 + .../decode/am/AMDecoder.java} | 95 +- .../decode/am/AMDecoderPanel.java} | 53 +- src/{ => module}/decode/am/AMEditor.java | 6 +- .../decode/am}/DecodeConfigAM.java | 5 +- .../decode/config/AuxDecodeConfiguration.java | 4 +- .../decode/config/DecodeConfigFactory.java | 11 +- .../decode/config/DecodeConfiguration.java | 19 +- .../decode/event}/ActivitySummaryFrame.java | 2 +- .../event}/ActivitySummaryProvider.java | 4 +- .../decode/event}/CallEvent.java | 52 +- .../event}/CallEventAliasCellRenderer.java | 2 +- .../decode/event}/CallEventModel.java | 213 +- .../decode/event}/CallEventPanel.java | 29 +- .../decode/event/ICallEventProvider.java | 9 + .../decode/event}/MessageActivityModel.java | 4 +- .../decode/event}/MessageActivityPanel.java | 32 +- .../decode/event}/MessageDetailsProvider.java | 4 +- .../decode/fleetsync1/Fleetsync1Message.java | 2 +- .../Fleetsync1MessageProcessor.java | 2 +- .../decode/fleetsync2/Fleetsync2Decoder.java | 90 +- .../fleetsync2/Fleetsync2DecoderPanel.java | 189 + .../Fleetsync2MessageProcessor.java | 21 +- .../decode/fleetsync2/FleetsyncCallEvent.java | 6 +- .../fleetsync2/FleetsyncDecoderState.java | 328 ++ .../decode/fleetsync2/FleetsyncMessage.java | 2 +- .../fleetsync2/FleetsyncMessageFilter.java | 2 +- .../fleetsync2/FleetsyncMessageType.java | 2 +- .../decode/lj1200/LJ1200CallEvent.java | 8 +- .../decode/lj1200/LJ1200Decoder.java | 76 +- .../decode/lj1200/LJ1200DecoderState.java | 144 + .../decode/lj1200/LJ1200Message.java | 12 +- .../decode/lj1200/LJ1200MessageFilter.java | 53 + .../decode/lj1200/LJ1200MessageProcessor.java | 32 +- src/module/decode/lj1200/LJ1200Panel.java | 111 + .../lj1200/LJ1200TransponderMessage.java | 4 +- .../decode/ltrnet}/DecodeConfigLTRNet.java | 5 +- .../decode/ltrnet/LTRCallEvent.java | 20 +- src/module/decode/ltrnet/LTRNetDecoder.java | 113 + .../decode/ltrnet/LTRNetDecoderPanel.java} | 72 +- .../decode/ltrnet/LTRNetDecoderState.java | 707 ++++ .../decode/ltrnet/LTRNetEditor.java | 11 +- .../decode/ltrnet/LTRNetISWMessage.java | 4 +- .../decode/ltrnet/LTRNetMessage.java | 6 +- .../decode/ltrnet/LTRNetMessageFilter.java | 2 +- .../decode/ltrnet/LTRNetMessageProcessor.java | 167 + .../decode/ltrnet/LTRNetOSWMessage.java | 109 +- .../ltrstandard}/DecodeConfigLTRStandard.java | 5 +- .../ltrstandard/LTRStandardDecoder.java | 78 + .../ltrstandard/LTRStandardDecoderPanel.java} | 78 +- .../ltrstandard/LTRStandardDecoderState.java | 398 ++ .../decode/ltrstandard/LTRStandardEditor.java | 13 +- .../ltrstandard/LTRStandardISWMessage.java | 2 +- .../ltrstandard/LTRStandardMessage.java | 4 +- .../ltrstandard/LTRStandardMessageFilter.java | 2 +- .../LTRStandardMessageProcessor.java | 40 +- .../ltrstandard/LTRStandardOSWMessage.java | 2 +- .../decode/mdc1200/MDCCallEvent.java | 8 +- .../decode/mdc1200/MDCDecoder.java | 74 +- .../decode/mdc1200/MDCDecoderState.java | 288 ++ .../decode/mdc1200/MDCMessage.java | 2 +- .../decode/mdc1200/MDCMessageFilter.java | 2 +- .../decode/mdc1200/MDCMessageProcessor.java | 2 +- .../decode/mdc1200/MDCMessageType.java | 2 +- src/{ => module}/decode/mdc1200/MDCPanel.java | 67 +- .../decode/mpt1327/DecodeConfigMPT1327.java | 102 + .../decode/mpt1327/MPT1327CallEvent.java | 6 +- .../decode/mpt1327/MPT1327ConfigEditor.java} | 93 +- .../decode/mpt1327/MPT1327Decoder.java | 141 +- .../decode/mpt1327/MPT1327DecoderPanel.java | 222 ++ .../decode/mpt1327/MPT1327DecoderState.java | 572 +++ .../decode/mpt1327/MPT1327Message.java | 2 +- .../decode/mpt1327/MPT1327MessageFilter.java | 4 +- .../mpt1327/MPT1327MessageProcessor.java | 32 +- .../decode/mpt1327/MPT1327TrafficPanel.java | 177 + .../decode/nbfm}/DecodeConfigNBFM.java | 5 +- src/module/decode/nbfm/NBFMDecoder.java | 37 + .../decode/nbfm/NBFMDecoderPanel.java} | 52 +- src/{ => module}/decode/nbfm/NBFMEditor.java | 6 +- .../decode/p25/C4FMFrameListener.java | 2 +- src/{ => module}/decode/p25/C4FMSlicer.java | 2 +- .../decode/p25/C4FMSymbolFilter.java | 3 +- .../decode/p25/DecodeConfigP25Phase1.java | 97 + src/{ => module}/decode/p25/P25CallEvent.java | 8 +- src/module/decode/p25/P25Decoder.java | 65 + .../decode/p25/P25DecoderPanel.java} | 114 +- src/module/decode/p25/P25DecoderState.java | 2932 ++++++++++++++ src/{ => module}/decode/p25/P25Editor.java | 24 +- .../decode/p25/P25Interleave.java | 2 +- .../decode/p25/P25MessageFramer.java | 51 +- .../decode/p25/P25MessageProcessor.java | 24 +- src/module/decode/p25/P25_C4FMDecoder.java | 239 ++ src/module/decode/p25/P25_LSMDecoder.java | 205 + .../decode/p25/Trellis_1_2_Rate.java | 2 +- .../decode/p25/Trellis_3_4_Rate.java | 2 +- .../decode/p25/audio/P25AudioModule.java | 220 ++ .../decode/p25/message/IBandIdentifier.java | 2 +- .../p25/message/IdentifierReceiver.java | 2 +- .../decode/p25/message/P25Message.java | 4 +- .../p25/message/filter/HDUMessageFilter.java | 6 +- .../p25/message/filter/LDUMessageFilter.java | 4 +- .../message/filter/P25MessageFilterSet.java | 4 +- .../p25/message/filter/PDUMessageFilter.java | 6 +- .../message/filter/TDULCMessageFilter.java | 6 +- .../p25/message/filter/TDUMessageFilter.java | 4 +- .../message/filter/TSBKMessageFilterSet.java | 12 +- .../decode/p25/message/hdu/HDUMessage.java | 9 +- .../decode/p25/message/ldu/LDU1Message.java | 11 +- .../decode/p25/message/ldu/LDU2Message.java | 7 +- .../decode/p25/message/ldu/LDUMessage.java | 7 +- .../ldu/lc/AdjacentSiteStatusBroadcast.java | 14 +- .../AdjacentSiteStatusBroadcastExplicit.java | 8 +- .../decode/p25/message/ldu/lc/CallAlert.java | 8 +- .../p25/message/ldu/lc/CallTermination.java | 8 +- .../ldu/lc/ChannelIdentifierUpdate.java | 10 +- .../lc/ChannelIdentifierUpdateExplicit.java | 10 +- .../ldu/lc/ExtendedFunctionCommand.java | 10 +- .../message/ldu/lc/GroupAffiliationQuery.java | 8 +- .../ldu/lc/GroupVoiceChannelUpdate.java | 12 +- .../lc/GroupVoiceChannelUpdateExplicit.java | 16 +- .../message/ldu/lc/GroupVoiceChannelUser.java | 8 +- .../message/ldu/lc/LDULCMessageFactory.java | 8 +- .../p25/message/ldu/lc/MessageUpdate.java | 8 +- .../ldu/lc/NetworkStatusBroadcast.java | 14 +- .../lc/NetworkStatusBroadcastExplicit.java | 12 +- .../ldu/lc/ProtectionParameterBroadcast.java | 10 +- .../message/ldu/lc/RFSSStatusBroadcast.java | 14 +- .../ldu/lc/RFSSStatusBroadcastExplicit.java | 14 +- .../lc/SecondaryControlChannelBroadcast.java | 14 +- ...ondaryControlChannelBroadcastExplicit.java | 14 +- .../p25/message/ldu/lc/StatusQuery.java | 8 +- .../p25/message/ldu/lc/StatusUpdate.java | 8 +- .../ldu/lc/SystemServiceBroadcast.java | 10 +- .../TelephoneInterconnectAnswerRequest.java | 10 +- ...TelephoneInterconnectVoiceChannelUser.java | 12 +- .../ldu/lc/UnitAuthenticationCommand.java | 8 +- .../ldu/lc/UnitRegistrationCommand.java | 8 +- .../ldu/lc/UnitToUnitAnswerRequest.java | 12 +- .../ldu/lc/UnitToUnitVoiceChannelUser.java | 12 +- .../ldu/lc/motorola/MotorolaOpcode_0.java | 8 +- .../decode/p25/message/pdu/PDUMessage.java | 16 +- .../p25/message/pdu/PDUMessageFactory.java | 50 +- .../pdu/UnitToUnitChannelGrantExtended.java | 10 +- .../pdu/confirmed/PDUConfirmedMessage.java | 11 +- .../confirmed/PDUConfirmedMessageFactory.java | 2 +- .../SNDCPActivateTDSContextAccept.java | 15 +- .../SNDCPActivateTDSContextReject.java | 6 +- .../SNDCPActivateTDSContextRequest.java | 6 +- .../confirmed/SNDCPDeactivateTDSContext.java | 8 +- .../message/pdu/confirmed/SNDCPUserData.java | 8 +- .../AdjacentStatusBroadcastExtended.java | 14 +- .../pdu/osp/control/CallAlertExtended.java | 8 +- .../GroupAffiliationQueryExtended.java | 8 +- .../GroupAffiliationResponseExtended.java | 10 +- .../osp/control/MessageUpdateExtended.java | 8 +- .../NetworkStatusBroadcastExtended.java | 16 +- .../control/ProtectionParameterBroadcast.java | 10 +- .../control/RFSSStatusBroadcastExtended.java | 14 +- .../control/RoamingAddressUpdateExtended.java | 10 +- .../pdu/osp/control/StatusQueryExtended.java | 8 +- .../pdu/osp/control/StatusUpdateExtended.java | 8 +- .../UnitRegistrationResponseExtended.java | 10 +- .../data/GroupDataChannelGrantExtended.java | 12 +- .../IndividualDataChannelGrantExtended.java | 10 +- .../voice/GroupVoiceChannelGrantExplicit.java | 12 +- ...phoneInterconnectChannelGrantExplicit.java | 12 +- ...nterconnectChannelGrantUpdateExplicit.java | 6 +- .../UnitToUnitAnswerRequestExplicit.java | 8 +- .../UnitToUnitVoiceChannelGrantExtended.java | 10 +- ...ToUnitVoiceChannelGrantUpdateExtended.java | 12 +- .../decode/p25/message/tdu/TDUMessage.java | 6 +- .../tdu/lc/AdjacentSiteStatusBroadcast.java | 14 +- .../AdjacentSiteStatusBroadcastExplicit.java | 8 +- .../decode/p25/message/tdu/lc/CallAlert.java | 6 +- .../p25/message/tdu/lc/CallTermination.java | 6 +- .../tdu/lc/ChannelIdentifierUpdate.java | 8 +- .../lc/ChannelIdentifierUpdateExplicit.java | 8 +- .../tdu/lc/ExtendedFunctionCommand.java | 8 +- .../message/tdu/lc/GroupAffiliationQuery.java | 6 +- .../tdu/lc/GroupVoiceChannelUpdate.java | 10 +- .../lc/GroupVoiceChannelUpdateExplicit.java | 14 +- .../message/tdu/lc/GroupVoiceChannelUser.java | 6 +- .../p25/message/tdu/lc/MessageUpdate.java | 6 +- .../tdu/lc/NetworkStatusBroadcast.java | 12 +- .../lc/NetworkStatusBroadcastExplicit.java | 12 +- .../tdu/lc/ProtectionParameterBroadcast.java | 8 +- .../message/tdu/lc/RFSSStatusBroadcast.java | 12 +- .../tdu/lc/RFSSStatusBroadcastExplicit.java | 12 +- .../lc/SecondaryControlChannelBroadcast.java | 12 +- ...ondaryControlChannelBroadcastExplicit.java | 12 +- .../p25/message/tdu/lc/StatusQuery.java | 6 +- .../p25/message/tdu/lc/StatusUpdate.java | 6 +- .../tdu/lc/SystemServiceBroadcast.java | 8 +- .../message/tdu/lc/TDULCMessageFactory.java | 6 +- .../message/tdu/lc/TDULinkControlMessage.java | 15 +- .../TelephoneInterconnectAnswerRequest.java | 8 +- ...TelephoneInterconnectVoiceChannelUser.java | 6 +- .../tdu/lc/UnitAuthenticationCommand.java | 6 +- .../tdu/lc/UnitRegistrationCommand.java | 6 +- .../tdu/lc/UnitToUnitAnswerRequest.java | 6 +- .../tdu/lc/UnitToUnitVoiceChannelUser.java | 6 +- .../decode/p25/message/tsbk/ChannelGrant.java | 8 +- .../p25/message/tsbk/GroupChannelGrant.java | 4 +- .../tsbk/GroupChannelGrantExplicit.java | 6 +- .../message/tsbk/GroupMultiChannelGrant.java | 8 +- .../p25/message/tsbk/ServiceMessage.java | 4 +- .../decode/p25/message/tsbk/TSBKMessage.java | 14 +- .../p25/message/tsbk/TSBKMessageFactory.java | 112 +- .../p25/message/tsbk/UnitChannelGrant.java | 4 +- ...ntrolChannelBaseStationIdentification.java | 8 +- .../message/tsbk/motorola/MotorolaOpcode.java | 2 +- .../tsbk/motorola/MotorolaTSBKMessage.java | 6 +- .../p25/message/tsbk/motorola/PatchGroup.java | 4 +- .../message/tsbk/motorola/PatchGroupAdd.java | 4 +- .../tsbk/motorola/PatchGroupDelete.java | 4 +- .../motorola/PatchGroupVoiceChannelGrant.java | 8 +- .../PatchGroupVoiceChannelGrantUpdate.java | 8 +- .../PlannedControlChannnelShutdown.java | 4 +- .../message/tsbk/motorola/SystemLoading.java | 6 +- ...afficChannelBaseStationIdentification.java | 4 +- .../tsbk/osp/control/AcknowledgeResponse.java | 8 +- .../osp/control/AdjacentStatusBroadcast.java | 12 +- .../osp/control/AuthenticationCommand.java | 8 +- .../message/tsbk/osp/control/CallAlert.java | 8 +- .../tsbk/osp/control/DenyResponse.java | 10 +- .../osp/control/ExtendedFunctionCommand.java | 10 +- .../osp/control/GroupAffiliationQuery.java | 8 +- .../osp/control/GroupAffiliationResponse.java | 10 +- .../tsbk/osp/control/IdentifierUpdate.java | 8 +- .../osp/control/IdentifierUpdateNonVUHF.java | 6 +- .../osp/control/IdentifierUpdateVUHF.java | 6 +- .../control/LocationRegistrationResponse.java | 10 +- .../tsbk/osp/control/MessageUpdate.java | 8 +- .../osp/control/NetworkStatusBroadcast.java | 14 +- .../control/ProtectionParameterUpdate.java | 10 +- .../tsbk/osp/control/QueuedResponse.java | 10 +- .../tsbk/osp/control/RFSSStatusBroadcast.java | 12 +- .../osp/control/RadioUnitMonitorCommand.java | 8 +- .../osp/control/RoamingAddressCommand.java | 12 +- .../SecondaryControlChannelBroadcast.java | 13 +- ...ondaryControlChannelBroadcastExplicit.java | 7 +- .../message/tsbk/osp/control/StatusQuery.java | 8 +- .../tsbk/osp/control/StatusUpdate.java | 8 +- .../tsbk/osp/control/SystemService.java | 2 +- .../osp/control/SystemServiceBroadcast.java | 10 +- .../osp/control/TimeAndDateAnnouncement.java | 8 +- .../UnitDeregistrationAcknowledge.java | 10 +- .../osp/control/UnitRegistrationCommand.java | 8 +- .../osp/control/UnitRegistrationResponse.java | 10 +- .../data/GroupDataChannelAnnouncement.java | 8 +- .../GroupDataChannelAnnouncementExplicit.java | 8 +- .../tsbk/osp/data/GroupDataChannelGrant.java | 8 +- .../osp/data/IndividualDataChannelGrant.java | 8 +- .../p25/message/tsbk/osp/data/SNDCPData.java | 6 +- .../SNDCPDataChannelAnnouncementExplicit.java | 8 +- .../tsbk/osp/data/SNDCPDataChannelGrant.java | 8 +- .../tsbk/osp/data/SNDCPDataPageRequest.java | 4 +- .../osp/voice/GroupVoiceChannelGrant.java | 8 +- .../voice/GroupVoiceChannelGrantUpdate.java | 8 +- .../GroupVoiceChannelGrantUpdateExplicit.java | 8 +- .../TelephoneInterconnectAnswerRequest.java | 10 +- ...elephoneInterconnectVoiceChannelGrant.java | 8 +- ...neInterconnectVoiceChannelGrantUpdate.java | 6 +- .../osp/voice/UnitToUnitAnswerRequest.java | 8 +- .../voice/UnitToUnitVoiceChannelGrant.java | 10 +- .../UnitToUnitVoiceChannelGrantUpdate.java | 8 +- .../p25/message/tsbk/vendor/VendorOpcode.java | 2 +- .../decode/p25/reference/DataUnitID.java | 2 +- .../decode/p25/reference/DenyReason.java | 2 +- .../decode/p25/reference/Digit.java | 2 +- .../decode/p25/reference/Encryption.java | 2 +- .../p25/reference/ExtendedFunction.java | 2 +- .../p25/reference/IPHeaderCompression.java | 2 +- .../decode/p25/reference/IPProtocol.java | 2 +- .../p25/reference/LinkControlOpcode.java | 2 +- .../p25/reference/MDPConfigurationOption.java | 2 +- .../decode/p25/reference/Opcode.java | 2 +- .../p25/reference/P25NetworkCallsign.java | 2 +- .../decode/p25/reference/PDUFormat.java | 2 +- .../decode/p25/reference/PDUType.java | 2 +- .../p25/reference/QueuedResponseReason.java | 2 +- .../decode/p25/reference/Response.java | 2 +- .../SNDCPActivationRejectReason.java | 2 +- .../decode/p25/reference/Service.java | 2 +- .../p25/reference/ServiceAccessPoint.java | 2 +- .../decode/p25/reference/StackOperation.java | 2 +- .../decode/p25/reference/Status.java | 2 +- .../decode/p25/reference/Vendor.java | 2 +- .../reference/VendorLinkControlOpcode.java | 2 +- .../passport}/DecodeConfigPassport.java | 5 +- .../decode/passport/PassportBand.java | 2 +- .../decode/passport/PassportCallEvent.java | 6 +- .../decode/passport/PassportDecoder.java | 103 + .../passport/PassportDecoderPanel.java} | 76 +- .../decode/passport/PassportDecoderState.java | 529 +++ .../decode/passport/PassportEditor.java | 6 +- .../decode/passport/PassportMessage.java | 4 +- .../passport/PassportMessageFilter.java | 2 +- .../passport/PassportMessageProcessor.java | 40 +- .../state/AlwaysUnsquelchedDecoderState.java | 83 + src/module/decode/state/ChangedAttribute.java | 29 + .../decode/state/ChannelCollectionPanel.java | 150 + .../decode/state/ChannelList.java} | 277 +- src/module/decode/state/ChannelState.java | 587 +++ .../decode/state/ChannelStatePanel.java | 457 +++ .../state/ChannelStateSelectionListener.java | 2 +- .../decode/state/DecoderPanel.java} | 135 +- src/module/decode/state/DecoderState.java | 186 + .../decode/state/DecoderStateEvent.java | 55 + .../state/IChangedAttributeListener.java | 8 + .../state/IChangedAttributeProvider.java | 9 + .../state/IDecoderStateEventListener.java | 8 + .../state/IDecoderStateEventProvider.java | 9 + src/module/decode/state/State.java | 83 + .../state/TrafficChannelAllocationEvent.java | 25 + .../decode/state/TrafficChannelManager.java | 255 ++ .../state/TrafficChannelStatusListener.java | 21 + .../decode/tait/Tait1200ANIMessage.java | 4 +- .../tait/Tait1200ANIMessageProcessor.java | 21 +- .../decode/tait/Tait1200CallEvent.java | 8 +- .../decode/tait/Tait1200Decoder.java | 92 +- .../decode/tait/Tait1200DecoderState.java | 236 ++ .../decode/tait/Tait1200GPSMessage.java | 4 +- .../tait/Tait1200GPSMessageProcessor.java | 23 +- .../decode/tait/Tait1200MessageFilter.java} | 54 +- .../decode/tait/Tait1200Panel.java} | 68 +- .../demodulate/am/AMDemodulatorModule.java | 112 + .../demodulate/fm/FMDemodulatorModule.java | 196 + .../record/ComplexBufferRecorderModule.java | 64 + .../record/RealBufferRecorderModule.java | 64 + .../swingx/mapviewer/AbstractTileFactory.java | 2 +- src/playlist/Playlist.java | 2 +- src/playlist/PlaylistNode.java | 4 +- src/record/RecorderManager.java | 4 +- src/record/wave/ComplexWaveRecorder.java | 60 +- src/record/wave/FloatWaveRecorder.java | 52 +- src/sample/SampleType.java | 8 + .../ComplexToFloatSampleConverter.java | 8 +- .../adapter/FloatArrayToComplexAdapter.java | 10 +- .../{ComplexSample.java => Complex.java} | 148 +- src/sample/complex/ComplexBuffer.java | 17 + .../ComplexBufferToStreamConverter.java | 30 + .../complex/ComplexSampleAssembler.java | 14 +- .../complex/ComplexSampleListListener.java | 2 +- src/sample/complex/ComplexSampleUtils.java | 14 +- .../ComplexToComplexBufferAssembler.java | 80 + .../complex/IComplexBufferListener.java | 8 + src/sample/complex/RateCounter_CB.java | 56 + src/sample/complex/ThreadKiller.java | 2 +- src/sample/real/IRealBufferListener.java | 8 + src/sample/real/IRealBufferProvider.java | 9 + src/sample/real/RateCounter_RB.java | 56 + .../real/RealBufferToStreamConverter.java | 28 + .../real/RealToRealBufferAssembler.java | 74 + src/source/ComplexSource.java | 8 +- src/source/FloatSource.java | 1 + src/source/RealSource.java | 1 + src/source/SampleProvider.java | 2 +- src/source/Source.java | 4 +- src/source/SourceComponentEditor.java | 9 +- src/source/SourceManager.java | 35 +- src/source/config/SourceConfigRecording.java | 9 + src/source/config/SourceConfigTuner.java | 16 + src/source/mixer/ComplexMixer.java | 8 +- src/source/mixer/MixerManager.java | 8 +- src/source/mixer/RealMixerSource.java | 4 +- .../recording/RecordingSourceManager.java | 24 +- src/source/tuner/TunerChannelSource.java | 201 +- src/source/tuner/TunerController.java | 5 +- src/source/tuner/TunerManager.java | 32 +- ....java => AutomaticFrequencyControl_R.java} | 6 +- .../AutomaticFrequencyControl_RB.java | 197 + .../frequency/FrequencyCorrectionControl.java | 4 +- .../IFrequencyCorrectionController.java | 8 + .../tuner/hackrf/HackRFTunerController.java | 61 +- .../tuner/rtl/RTL2832TunerController.java | 102 +- .../tuner/rtl/e4k/E4KTunerController.java | 8 +- .../r820t/R820TTunerConfigurationPanel.java | 116 +- .../tuner/rtl/r820t/R820TTunerController.java | 15 +- src/source/wave/ComplexWaveSource.java | 13 +- src/source/wave/FloatWaveSource.java | 1 + src/source/wave/WaveSource.java | 1 + src/spectrum/ChannelSpectrumPanel.java | 22 +- src/spectrum/DFTProcessor.java | 62 +- src/spectrum/OverlayPanel.java | 11 +- src/spectrum/SpectralDisplayPanel.java | 30 +- src/util/ArcTangent.java | 4 +- src/util/Oscillator.java | 44 +- 570 files changed, 21578 insertions(+), 17012 deletions(-) rename imports/{jmbe-interface-0.2.0.jar => jmbe-interface-0.2.1.jar} (77%) create mode 100644 src/.directory create mode 100644 src/alias/Metadata.java create mode 100644 src/alias/MetadataReset.java create mode 100644 src/alias/MetadataType.java create mode 100644 src/audio/AudioChannelPanel.java create mode 100644 src/audio/AudioEvent.java create mode 100644 src/audio/AudioException.java create mode 100644 src/audio/AudioManager.java create mode 100644 src/audio/AudioModule.java delete mode 100644 src/audio/AudioOutputImpl.java delete mode 100644 src/audio/AudioOutputManager.java create mode 100644 src/audio/AudioPacketRateCounter.java create mode 100644 src/audio/AudioPanel.java create mode 100644 src/audio/IAudioController.java delete mode 100644 src/audio/IAudioOutput.java create mode 100644 src/audio/IAudioPacketProvider.java create mode 100644 src/audio/metadata/AudioMetadata.java create mode 100644 src/audio/metadata/IMetadataListener.java create mode 100644 src/audio/metadata/IMetadataProvider.java create mode 100644 src/audio/output/AudioOutput.java rename src/audio/{ => output}/MonoAudioOutput.java (60%) create mode 100644 src/audio/squelch/ISquelchStateListener.java create mode 100644 src/audio/squelch/ISquelchStateProvider.java create mode 100644 src/audio/squelch/ManualSquelchController.java create mode 100644 src/audio/squelch/SquelchController.java create mode 100644 src/audio/squelch/SquelchMode.java create mode 100644 src/audio/squelch/SquelchState.java rename src/{decode/tait/Tait1200ChannelState.java => audio/squelch/TrunkingSquelchController.java} (50%) create mode 100644 src/controller/NamingThreadFactory.java create mode 100644 src/controller/channel/IChannelEventListener.java create mode 100644 src/controller/channel/IChannelEventProvider.java rename src/controller/channel/{ => map}/ChannelMap.java (98%) rename src/controller/channel/{ => map}/ChannelMapEditor.java (97%) rename src/controller/channel/{ => map}/ChannelMapList.java (98%) rename src/controller/channel/{ => map}/ChannelMapListNode.java (98%) rename src/controller/channel/{ => map}/ChannelMapModel.java (99%) rename src/controller/channel/{ => map}/ChannelMapNode.java (98%) rename src/controller/channel/{ => map}/ChannelRange.java (98%) delete mode 100644 src/controller/state/AuxChannelState.java delete mode 100644 src/controller/state/ChannelState.java delete mode 100644 src/controller/state/ChannelStatePanel.java delete mode 100644 src/decode/DecodeEditorFactory.java delete mode 100644 src/decode/Decoder.java delete mode 100644 src/decode/DecoderFactory.java delete mode 100644 src/decode/am/AMChannelState.java delete mode 100644 src/decode/am/AMDecoder.java delete mode 100644 src/decode/am/AMPanel.java delete mode 100644 src/decode/fleetsync2/FleetsyncActivitySummary.java delete mode 100644 src/decode/fleetsync2/FleetsyncChannelState.java delete mode 100644 src/decode/lj1200/LJ1200ChannelState.java delete mode 100644 src/decode/lj1200/LJ1200MessageFilter.java delete mode 100644 src/decode/ltrnet/LTRNetActivitySummary.java delete mode 100644 src/decode/ltrnet/LTRNetChannelState.java delete mode 100644 src/decode/ltrnet/LTRNetDecoder.java delete mode 100644 src/decode/ltrnet/LTRNetMessageProcessor.java delete mode 100644 src/decode/ltrstandard/LTRChannelState.java delete mode 100644 src/decode/ltrstandard/LTRStandardActivitySummary.java delete mode 100644 src/decode/ltrstandard/LTRStandardDecoder.java delete mode 100644 src/decode/mdc1200/MDCActivitySummary.java delete mode 100644 src/decode/mdc1200/MDCChannelState.java delete mode 100644 src/decode/mpt1327/MPT1327ActivitySummary.java delete mode 100644 src/decode/mpt1327/MPT1327ChannelState.java delete mode 100644 src/decode/mpt1327/MPT1327Panel.java delete mode 100644 src/decode/mpt1327/MPT1327TrafficPanel.java delete mode 100644 src/decode/nbfm/NBFMChannelState.java delete mode 100644 src/decode/nbfm/NBFMDecoder.java delete mode 100644 src/decode/nbfm/NBFMPanel.java delete mode 100644 src/decode/p25/P25ActivitySummary.java delete mode 100644 src/decode/p25/P25ChannelState.java delete mode 100644 src/decode/p25/P25Decoder.java delete mode 100644 src/decode/p25/audio/IMBETargetDataLine.java delete mode 100644 src/decode/p25/audio/P25AudioCodecTest.java delete mode 100644 src/decode/p25/audio/P25AudioOutput.java delete mode 100644 src/decode/passport/PassportActivitySummary.java delete mode 100644 src/decode/passport/PassportChannelState.java delete mode 100644 src/decode/passport/PassportDecoder.java delete mode 100644 src/decode/tait/Tait1200MessageFilter.java rename src/{decode/tait/Tait1200ActivitySummary.java => dsp/am/AMDemodulator_CB.java} (52%) rename src/dsp/filter/{ComplexFIRFilter.java => ComplexFIRFilterOld.java} (90%) rename src/dsp/filter/{ => cic}/ComplexPrimeCICDecimate.java (75%) rename src/{decode/lj1200/LJ1200ActivitySummary.java => dsp/filter/dc/AveragingDCRemovalFilter_RB.java} (58%) rename src/{audio/SquelchListener.java => dsp/filter/dc/DCRemovalFilter.java} (68%) rename src/dsp/filter/{DCRemovalFilter.java => dc/DCRemovalFilterLyons.java} (87%) create mode 100644 src/dsp/filter/dc/DCRemovalFilter_RB.java rename src/dsp/filter/{DCRemovalFilter2.java => dc/DCRemovalFilter_R_R.java} (70%) rename src/dsp/filter/{DCRemovalFilter3.java => dc/IIRSinglePoleDCRemovalFilter.java} (86%) create mode 100644 src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java create mode 100644 src/dsp/filter/fir/FIRFilter.java create mode 100644 src/dsp/filter/fir/complex/ComplexFIRFilter.java create mode 100644 src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java create mode 100644 src/dsp/filter/fir/real/RealFIRFilter.java create mode 100644 src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java create mode 100644 src/dsp/filter/fir/real/RealFIRFilter_R_R.java create mode 100644 src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java create mode 100644 src/dsp/filter/halfband/real/HalfBandFilter.java create mode 100644 src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java delete mode 100644 src/dsp/filter/interpolator/QPSKInterpolator.java create mode 100644 src/dsp/filter/interpolator/RealInterpolator.java create mode 100644 src/dsp/fm/FMDemodulator.java create mode 100644 src/dsp/fm/FMDemodulator_CB.java rename src/dsp/{nbfm => fm}/FMDiscriminator.java (90%) rename src/dsp/{nbfm => fm}/FilteringNBFMDemodulator.java (87%) rename src/dsp/{nbfm => fm}/NBFMDemodulator.java (85%) rename src/dsp/gain/{RealAutomaticGainControl.java => AutomaticGainControl.java} (88%) create mode 100644 src/dsp/gain/AutomaticGainControl_RB.java create mode 100644 src/instrument/tap/stream/ComplexSampleTap.java create mode 100644 src/instrument/tap/stream/ComplexSampleTapViewPanel.java create mode 100644 src/instrument/tap/stream/FloatBufferTap.java create mode 100644 src/message/IMessageListener.java create mode 100644 src/message/IMessageProvider.java delete mode 100644 src/message/MessageFilterFactory.java create mode 100644 src/module/Module.java create mode 100644 src/module/ProcessingChain.java rename src/{controller/channel/ProcessingChain.java => module/ProcessingChainOld.java} (76%) rename src/{ => module}/decode/AuxDecodeComponentEditor.java (97%) rename src/{ => module}/decode/DecodeComponentEditor.java (88%) rename src/{ => module}/decode/DecodeEditor.java (91%) create mode 100644 src/module/decode/Decoder.java create mode 100644 src/module/decode/DecoderFactory.java rename src/{ => module}/decode/DecoderType.java (96%) create mode 100644 src/module/decode/PrimaryDecoder.java rename src/{decode/config/DecodeConfigMPT1327.java => module/decode/am/AMDecoder.java} (55%) rename src/{decode/tait/Tait1200Panel.java => module/decode/am/AMDecoderPanel.java} (57%) rename src/{ => module}/decode/am/AMEditor.java (90%) rename src/{decode/config => module/decode/am}/DecodeConfigAM.java (91%) rename src/{ => module}/decode/config/AuxDecodeConfiguration.java (96%) rename src/{ => module}/decode/config/DecodeConfigFactory.java (82%) rename src/{ => module}/decode/config/DecodeConfiguration.java (74%) rename src/{controller/activity => module/decode/event}/ActivitySummaryFrame.java (98%) rename src/{controller/activity => module/decode/event}/ActivitySummaryProvider.java (93%) rename src/{controller/activity => module/decode/event}/CallEvent.java (84%) rename src/{controller/activity => module/decode/event}/CallEventAliasCellRenderer.java (98%) rename src/{controller/activity => module/decode/event}/CallEventModel.java (52%) rename src/{controller/activity => module/decode/event}/CallEventPanel.java (74%) create mode 100644 src/module/decode/event/ICallEventProvider.java rename src/{controller/activity => module/decode/event}/MessageActivityModel.java (97%) rename src/{controller/activity => module/decode/event}/MessageActivityPanel.java (90%) rename src/{controller/activity => module/decode/event}/MessageDetailsProvider.java (97%) rename src/{ => module}/decode/fleetsync1/Fleetsync1Message.java (99%) rename src/{ => module}/decode/fleetsync1/Fleetsync1MessageProcessor.java (98%) rename src/{ => module}/decode/fleetsync2/Fleetsync2Decoder.java (68%) create mode 100644 src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java rename src/{ => module}/decode/fleetsync2/Fleetsync2MessageProcessor.java (77%) rename src/{ => module}/decode/fleetsync2/FleetsyncCallEvent.java (98%) create mode 100644 src/module/decode/fleetsync2/FleetsyncDecoderState.java rename src/{ => module}/decode/fleetsync2/FleetsyncMessage.java (99%) rename src/{ => module}/decode/fleetsync2/FleetsyncMessageFilter.java (97%) rename src/{ => module}/decode/fleetsync2/FleetsyncMessageType.java (97%) rename src/{ => module}/decode/lj1200/LJ1200CallEvent.java (95%) rename src/{ => module}/decode/lj1200/LJ1200Decoder.java (76%) create mode 100644 src/module/decode/lj1200/LJ1200DecoderState.java rename src/{ => module}/decode/lj1200/LJ1200Message.java (97%) create mode 100644 src/module/decode/lj1200/LJ1200MessageFilter.java rename src/{ => module}/decode/lj1200/LJ1200MessageProcessor.java (71%) create mode 100644 src/module/decode/lj1200/LJ1200Panel.java rename src/{ => module}/decode/lj1200/LJ1200TransponderMessage.java (98%) rename src/{decode/config => module/decode/ltrnet}/DecodeConfigLTRNet.java (92%) rename src/{ => module}/decode/ltrnet/LTRCallEvent.java (89%) create mode 100644 src/module/decode/ltrnet/LTRNetDecoder.java rename src/{decode/ltrnet/LTRNetPanel.java => module/decode/ltrnet/LTRNetDecoderPanel.java} (66%) create mode 100644 src/module/decode/ltrnet/LTRNetDecoderState.java rename src/{ => module}/decode/ltrnet/LTRNetEditor.java (90%) rename src/{ => module}/decode/ltrnet/LTRNetISWMessage.java (99%) rename src/{ => module}/decode/ltrnet/LTRNetMessage.java (98%) rename src/{ => module}/decode/ltrnet/LTRNetMessageFilter.java (98%) create mode 100644 src/module/decode/ltrnet/LTRNetMessageProcessor.java rename src/{ => module}/decode/ltrnet/LTRNetOSWMessage.java (83%) rename src/{decode/config => module/decode/ltrstandard}/DecodeConfigLTRStandard.java (92%) create mode 100644 src/module/decode/ltrstandard/LTRStandardDecoder.java rename src/{decode/ltrstandard/LTRPanel.java => module/decode/ltrstandard/LTRStandardDecoderPanel.java} (62%) create mode 100644 src/module/decode/ltrstandard/LTRStandardDecoderState.java rename src/{ => module}/decode/ltrstandard/LTRStandardEditor.java (90%) rename src/{ => module}/decode/ltrstandard/LTRStandardISWMessage.java (99%) rename src/{ => module}/decode/ltrstandard/LTRStandardMessage.java (98%) rename src/{ => module}/decode/ltrstandard/LTRStandardMessageFilter.java (97%) rename src/{ => module}/decode/ltrstandard/LTRStandardMessageProcessor.java (67%) rename src/{ => module}/decode/ltrstandard/LTRStandardOSWMessage.java (99%) rename src/{ => module}/decode/mdc1200/MDCCallEvent.java (96%) rename src/{ => module}/decode/mdc1200/MDCDecoder.java (79%) create mode 100644 src/module/decode/mdc1200/MDCDecoderState.java rename src/{ => module}/decode/mdc1200/MDCMessage.java (99%) rename src/{ => module}/decode/mdc1200/MDCMessageFilter.java (97%) rename src/{ => module}/decode/mdc1200/MDCMessageProcessor.java (99%) rename src/{ => module}/decode/mdc1200/MDCMessageType.java (97%) rename src/{ => module}/decode/mdc1200/MDCPanel.java (75%) create mode 100644 src/module/decode/mpt1327/DecodeConfigMPT1327.java rename src/{ => module}/decode/mpt1327/MPT1327CallEvent.java (96%) rename src/{decode/mpt1327/MPT1327Editor.java => module/decode/mpt1327/MPT1327ConfigEditor.java} (50%) rename src/{ => module}/decode/mpt1327/MPT1327Decoder.java (70%) create mode 100644 src/module/decode/mpt1327/MPT1327DecoderPanel.java create mode 100644 src/module/decode/mpt1327/MPT1327DecoderState.java rename src/{ => module}/decode/mpt1327/MPT1327Message.java (99%) rename src/{ => module}/decode/mpt1327/MPT1327MessageFilter.java (93%) rename src/{ => module}/decode/mpt1327/MPT1327MessageProcessor.java (66%) create mode 100644 src/module/decode/mpt1327/MPT1327TrafficPanel.java rename src/{decode/config => module/decode/nbfm}/DecodeConfigNBFM.java (90%) create mode 100644 src/module/decode/nbfm/NBFMDecoder.java rename src/{decode/lj1200/LJ1200Panel.java => module/decode/nbfm/NBFMDecoderPanel.java} (62%) rename src/{ => module}/decode/nbfm/NBFMEditor.java (91%) rename src/{ => module}/decode/p25/C4FMFrameListener.java (92%) rename src/{ => module}/decode/p25/C4FMSlicer.java (98%) rename src/{ => module}/decode/p25/C4FMSymbolFilter.java (99%) create mode 100644 src/module/decode/p25/DecodeConfigP25Phase1.java rename src/{ => module}/decode/p25/P25CallEvent.java (95%) create mode 100644 src/module/decode/p25/P25Decoder.java rename src/{decode/p25/P25Panel.java => module/decode/p25/P25DecoderPanel.java} (57%) create mode 100644 src/module/decode/p25/P25DecoderState.java rename src/{ => module}/decode/p25/P25Editor.java (81%) rename src/{ => module}/decode/p25/P25Interleave.java (99%) rename src/{ => module}/decode/p25/P25MessageFramer.java (92%) rename src/{ => module}/decode/p25/P25MessageProcessor.java (78%) create mode 100644 src/module/decode/p25/P25_C4FMDecoder.java create mode 100644 src/module/decode/p25/P25_LSMDecoder.java rename src/{ => module}/decode/p25/Trellis_1_2_Rate.java (99%) rename src/{ => module}/decode/p25/Trellis_3_4_Rate.java (99%) create mode 100644 src/module/decode/p25/audio/P25AudioModule.java rename src/{ => module}/decode/p25/message/IBandIdentifier.java (92%) rename src/{ => module}/decode/p25/message/IdentifierReceiver.java (91%) rename src/{ => module}/decode/p25/message/P25Message.java (97%) rename src/{ => module}/decode/p25/message/filter/HDUMessageFilter.java (79%) rename src/{ => module}/decode/p25/message/filter/LDUMessageFilter.java (86%) rename src/{ => module}/decode/p25/message/filter/P25MessageFilterSet.java (86%) rename src/{ => module}/decode/p25/message/filter/PDUMessageFilter.java (89%) rename src/{ => module}/decode/p25/message/filter/TDULCMessageFilter.java (80%) rename src/{ => module}/decode/p25/message/filter/TDUMessageFilter.java (86%) rename src/{ => module}/decode/p25/message/filter/TSBKMessageFilterSet.java (93%) rename src/{ => module}/decode/p25/message/hdu/HDUMessage.java (98%) rename src/{ => module}/decode/p25/message/ldu/LDU1Message.java (97%) rename src/{ => module}/decode/p25/message/ldu/LDU2Message.java (98%) rename src/{ => module}/decode/p25/message/ldu/LDUMessage.java (95%) rename src/{ => module}/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java (90%) rename src/{ => module}/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java (93%) rename src/{ => module}/decode/p25/message/ldu/lc/CallAlert.java (89%) rename src/{ => module}/decode/p25/message/ldu/lc/CallTermination.java (85%) rename src/{ => module}/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/GroupAffiliationQuery.java (88%) rename src/{ => module}/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java (93%) rename src/{ => module}/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java (93%) rename src/{ => module}/decode/p25/message/ldu/lc/LDULCMessageFactory.java (95%) rename src/{ => module}/decode/p25/message/ldu/lc/MessageUpdate.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java (89%) rename src/{ => module}/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java (92%) rename src/{ => module}/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java (88%) rename src/{ => module}/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java (89%) rename src/{ => module}/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/StatusQuery.java (88%) rename src/{ => module}/decode/p25/message/ldu/lc/StatusUpdate.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/SystemServiceBroadcast.java (88%) rename src/{ => module}/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java (92%) rename src/{ => module}/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java (89%) rename src/{ => module}/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/UnitRegistrationCommand.java (91%) rename src/{ => module}/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java (88%) rename src/{ => module}/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java (88%) rename src/{ => module}/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java (88%) rename src/{ => module}/decode/p25/message/pdu/PDUMessage.java (91%) rename src/{ => module}/decode/p25/message/pdu/PDUMessageFactory.java (66%) rename src/{ => module}/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java (95%) rename src/{ => module}/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java (95%) rename src/{ => module}/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java (97%) rename src/{ => module}/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java (93%) rename src/{ => module}/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java (92%) rename src/{ => module}/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java (97%) rename src/{ => module}/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java (89%) rename src/{ => module}/decode/p25/message/pdu/confirmed/SNDCPUserData.java (98%) rename src/{ => module}/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java (92%) rename src/{ => module}/decode/p25/message/pdu/osp/control/CallAlertExtended.java (91%) rename src/{ => module}/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java (92%) rename src/{ => module}/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java (94%) rename src/{ => module}/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java (93%) rename src/{ => module}/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java (90%) rename src/{ => module}/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java (93%) rename src/{ => module}/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java (92%) rename src/{ => module}/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java (97%) rename src/{ => module}/decode/p25/message/pdu/osp/control/StatusQueryExtended.java (92%) rename src/{ => module}/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java (93%) rename src/{ => module}/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java (92%) rename src/{ => module}/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java (94%) rename src/{ => module}/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java (65%) rename src/{ => module}/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java (94%) rename src/{ => module}/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java (94%) rename src/{ => module}/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java (78%) rename src/{ => module}/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java (94%) rename src/{ => module}/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java (65%) rename src/{ => module}/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java (95%) rename src/{ => module}/decode/p25/message/tdu/TDUMessage.java (76%) rename src/{ => module}/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java (90%) rename src/{ => module}/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/CallAlert.java (92%) rename src/{ => module}/decode/p25/message/tdu/lc/CallTermination.java (90%) rename src/{ => module}/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/GroupAffiliationQuery.java (92%) rename src/{ => module}/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java (95%) rename src/{ => module}/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java (92%) rename src/{ => module}/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java (95%) rename src/{ => module}/decode/p25/message/tdu/lc/MessageUpdate.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java (90%) rename src/{ => module}/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java (92%) rename src/{ => module}/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java (91%) rename src/{ => module}/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java (91%) rename src/{ => module}/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java (93%) rename src/{ => module}/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java (92%) rename src/{ => module}/decode/p25/message/tdu/lc/StatusQuery.java (92%) rename src/{ => module}/decode/p25/message/tdu/lc/StatusUpdate.java (94%) rename src/{ => module}/decode/p25/message/tdu/lc/SystemServiceBroadcast.java (91%) rename src/{ => module}/decode/p25/message/tdu/lc/TDULCMessageFactory.java (97%) rename src/{ => module}/decode/p25/message/tdu/lc/TDULinkControlMessage.java (96%) rename src/{ => module}/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java (94%) rename src/{ => module}/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java (95%) rename src/{ => module}/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java (94%) rename src/{ => module}/decode/p25/message/tdu/lc/UnitRegistrationCommand.java (94%) rename src/{ => module}/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java (95%) rename src/{ => module}/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java (95%) rename src/{ => module}/decode/p25/message/tsbk/ChannelGrant.java (85%) rename src/{ => module}/decode/p25/message/tsbk/GroupChannelGrant.java (92%) rename src/{ => module}/decode/p25/message/tsbk/GroupChannelGrantExplicit.java (92%) rename src/{ => module}/decode/p25/message/tsbk/GroupMultiChannelGrant.java (91%) rename src/{ => module}/decode/p25/message/tsbk/ServiceMessage.java (88%) rename src/{ => module}/decode/p25/message/tsbk/TSBKMessage.java (91%) rename src/{ => module}/decode/p25/message/tsbk/TSBKMessageFactory.java (67%) rename src/{ => module}/decode/p25/message/tsbk/UnitChannelGrant.java (91%) rename src/{ => module}/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java (90%) rename src/{ => module}/decode/p25/message/tsbk/motorola/MotorolaOpcode.java (98%) rename src/{ => module}/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java (84%) rename src/{ => module}/decode/p25/message/tsbk/motorola/PatchGroup.java (94%) rename src/{ => module}/decode/p25/message/tsbk/motorola/PatchGroupAdd.java (79%) rename src/{ => module}/decode/p25/message/tsbk/motorola/PatchGroupDelete.java (79%) rename src/{ => module}/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java (94%) rename src/{ => module}/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java (94%) rename src/{ => module}/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java (83%) rename src/{ => module}/decode/p25/message/tsbk/motorola/SystemLoading.java (79%) rename src/{ => module}/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java (92%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java (90%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java (90%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java (86%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/CallAlert.java (84%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/DenyResponse.java (87%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java (86%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java (85%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java (87%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java (82%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java (88%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java (89%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java (86%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/MessageUpdate.java (86%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java (86%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java (84%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/QueuedResponse.java (88%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java (88%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java (86%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java (82%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java (90%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java (91%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/StatusQuery.java (84%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/StatusUpdate.java (87%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/SystemService.java (93%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java (83%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java (94%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java (83%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java (85%) rename src/{ => module}/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java (85%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java (68%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java (69%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java (67%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java (68%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/SNDCPData.java (82%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java (95%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java (94%) rename src/{ => module}/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java (92%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java (68%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java (68%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java (69%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java (88%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java (84%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java (75%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java (68%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java (78%) rename src/{ => module}/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java (67%) rename src/{ => module}/decode/p25/message/tsbk/vendor/VendorOpcode.java (98%) rename src/{ => module}/decode/p25/reference/DataUnitID.java (98%) rename src/{ => module}/decode/p25/reference/DenyReason.java (95%) rename src/{ => module}/decode/p25/reference/Digit.java (93%) rename src/{ => module}/decode/p25/reference/Encryption.java (96%) rename src/{ => module}/decode/p25/reference/ExtendedFunction.java (97%) rename src/{ => module}/decode/p25/reference/IPHeaderCompression.java (94%) rename src/{ => module}/decode/p25/reference/IPProtocol.java (99%) rename src/{ => module}/decode/p25/reference/LinkControlOpcode.java (99%) rename src/{ => module}/decode/p25/reference/MDPConfigurationOption.java (93%) rename src/{ => module}/decode/p25/reference/Opcode.java (99%) rename src/{ => module}/decode/p25/reference/P25NetworkCallsign.java (97%) rename src/{ => module}/decode/p25/reference/PDUFormat.java (96%) rename src/{ => module}/decode/p25/reference/PDUType.java (96%) rename src/{ => module}/decode/p25/reference/QueuedResponseReason.java (94%) rename src/{ => module}/decode/p25/reference/Response.java (85%) rename src/{ => module}/decode/p25/reference/SNDCPActivationRejectReason.java (92%) rename src/{ => module}/decode/p25/reference/Service.java (97%) rename src/{ => module}/decode/p25/reference/ServiceAccessPoint.java (98%) rename src/{ => module}/decode/p25/reference/StackOperation.java (85%) rename src/{ => module}/decode/p25/reference/Status.java (85%) rename src/{ => module}/decode/p25/reference/Vendor.java (99%) rename src/{ => module}/decode/p25/reference/VendorLinkControlOpcode.java (98%) rename src/{decode/config => module/decode/passport}/DecodeConfigPassport.java (90%) rename src/{ => module}/decode/passport/PassportBand.java (98%) rename src/{ => module}/decode/passport/PassportCallEvent.java (97%) create mode 100644 src/module/decode/passport/PassportDecoder.java rename src/{decode/passport/PassportPanel.java => module/decode/passport/PassportDecoderPanel.java} (72%) create mode 100644 src/module/decode/passport/PassportDecoderState.java rename src/{ => module}/decode/passport/PassportEditor.java (90%) rename src/{ => module}/decode/passport/PassportMessage.java (99%) rename src/{ => module}/decode/passport/PassportMessageFilter.java (98%) rename src/{ => module}/decode/passport/PassportMessageProcessor.java (77%) create mode 100644 src/module/decode/state/AlwaysUnsquelchedDecoderState.java create mode 100644 src/module/decode/state/ChangedAttribute.java create mode 100644 src/module/decode/state/ChannelCollectionPanel.java rename src/{controller/state/ChannelStateList.java => module/decode/state/ChannelList.java} (56%) create mode 100644 src/module/decode/state/ChannelState.java create mode 100644 src/module/decode/state/ChannelStatePanel.java rename src/{controller => module/decode}/state/ChannelStateSelectionListener.java (97%) rename src/{controller/state/AuxStatePanel.java => module/decode/state/DecoderPanel.java} (50%) create mode 100644 src/module/decode/state/DecoderState.java create mode 100644 src/module/decode/state/DecoderStateEvent.java create mode 100644 src/module/decode/state/IChangedAttributeListener.java create mode 100644 src/module/decode/state/IChangedAttributeProvider.java create mode 100644 src/module/decode/state/IDecoderStateEventListener.java create mode 100644 src/module/decode/state/IDecoderStateEventProvider.java create mode 100644 src/module/decode/state/State.java create mode 100644 src/module/decode/state/TrafficChannelAllocationEvent.java create mode 100644 src/module/decode/state/TrafficChannelManager.java create mode 100644 src/module/decode/state/TrafficChannelStatusListener.java rename src/{ => module}/decode/tait/Tait1200ANIMessage.java (99%) rename src/{ => module}/decode/tait/Tait1200ANIMessageProcessor.java (78%) rename src/{ => module}/decode/tait/Tait1200CallEvent.java (95%) rename src/{ => module}/decode/tait/Tait1200Decoder.java (69%) create mode 100644 src/module/decode/tait/Tait1200DecoderState.java rename src/{ => module}/decode/tait/Tait1200GPSMessage.java (99%) rename src/{ => module}/decode/tait/Tait1200GPSMessageProcessor.java (75%) rename src/{decode/config/DecodeConfigP25Phase1.java => module/decode/tait/Tait1200MessageFilter.java} (56%) rename src/{decode/fleetsync2/FleetsyncPanel.java => module/decode/tait/Tait1200Panel.java} (73%) create mode 100644 src/module/demodulate/am/AMDemodulatorModule.java create mode 100644 src/module/demodulate/fm/FMDemodulatorModule.java create mode 100644 src/module/record/ComplexBufferRecorderModule.java create mode 100644 src/module/record/RealBufferRecorderModule.java create mode 100644 src/sample/SampleType.java rename src/sample/complex/{ComplexSample.java => Complex.java} (58%) create mode 100644 src/sample/complex/ComplexBufferToStreamConverter.java create mode 100644 src/sample/complex/ComplexToComplexBufferAssembler.java create mode 100644 src/sample/complex/IComplexBufferListener.java create mode 100644 src/sample/complex/RateCounter_CB.java create mode 100644 src/sample/real/IRealBufferListener.java create mode 100644 src/sample/real/IRealBufferProvider.java create mode 100644 src/sample/real/RateCounter_RB.java create mode 100644 src/sample/real/RealBufferToStreamConverter.java create mode 100644 src/sample/real/RealToRealBufferAssembler.java rename src/source/tuner/frequency/{AutomaticFrequencyControl.java => AutomaticFrequencyControl_R.java} (95%) create mode 100644 src/source/tuner/frequency/AutomaticFrequencyControl_RB.java create mode 100644 src/source/tuner/frequency/IFrequencyCorrectionController.java diff --git a/imports/jmbe-interface-0.2.0.jar b/imports/jmbe-interface-0.2.1.jar similarity index 77% rename from imports/jmbe-interface-0.2.0.jar rename to imports/jmbe-interface-0.2.1.jar index 85add8bd53e3841db57aecc621adeeabf9c7e455..9963638a0a2aa5825cb565af50afbc2cba992161 100644 GIT binary patch delta 3154 zcmZvecTf{Z8^%Kiks=r=N>TBED1_cZhtP{biVy@T0YVXMv>=hLAT=b?q=qU)x+riI zrS}L5B3(oz6cGrJ03V)n=XW!=JG=Yt?DIam@67)3iwc?yWxr}nM}GnUU@m*stbCtN#sD+aCYdYxjpwWX)aIX;;>s*!v;y1|cbu0-1HCdjkd9Sm|~(k-83 zus1z)_s=IT#9-aZO;#HN?|OF2+@_L1M5E`5zw7wA`SD;2v#n&iixF_Lj#qMPOx&j_(Y5Wan_#Bh}ufLN&{B&p)8d*XP}*FBk##W z{%39}=@Vc|;Pe{c3@`H*{eGlVYe%QY4jJUsKAygQ;XX{p8Y!FzJyX<{A{>HoP`b=q zzd%@8!$~38GgG(AM)M{zuE54zcbx1Iw|Xu6>@O{))ltt{bt6+{1FyWnZ0=2kIPp1c zq2^|xBp!vZVeN`~sK*;x(0c1F`&_uqKjkn3e!d;!M^}MH=5p(cUjjzb(E?W?=%4E-qSLKSAbg$?)6*YRE?@L)epob z#r@OFqoEI4?pgU}uCiwY2b8S)lQ)L*G%!)4rz-QXcV-2fe_Yig)Dbr&p?FYO_x1;!r zgj5yN@fBAMdUN%T_V(Vyg5DDqMCck*mjoIxNXx_$?fi*0q|Q1q|CC_LpII^xV++4~ zon>bhEt{3UnM!?ai|%>O&=^LV2^t3kvu^4A@f>X%`MB9k_iAXDdJ7QG@PH;`Tp7C? zWVR72Pl-Jrcisw#l!ZI%w<0?=Frsa`cVks3Th!IY`5gMi{68F268yG{>ifEbPXy2F zgmY(nVCfj_PTT$L0pFbQAsZjY?Dz3^4hS?I2F5JB`N4m;8`)iIIrW@wfTv>7vY|Fz zI0WsMNHCxrbwm}tBFYthnbkwg^UAi3CUZ7n=y^vF2h(|5Z8eXjum!dnRT?@BKU{{> zNtLIIPT=y5c8{FXG=2J_?@OGaS&^9;MFFqYi?_dv%T4UCp>{uiD-qY)z-$T3dfh<; z&6?|VEms3xur~Rw&5d|TekI%}f7h}pMK5A_>I$;4qswJEF=Y88!L}~E*^PN=U3dMzX z*gK{Gs+yM(C@4F5R%K8gnJK|PqfVOV9Om+oO*WJ;oVd+FnIUK9iA=^#OP)c7$MO$` z%=9bjc+P)&rr2kNf7ip;e7H7`dj0-=h9<{lpQfFl!`f`L7yt0Hk7E0ymLn&p#o@%Q zLMa$@;fj^p8g>Sj%^jafR03s9c?hvPN@f*X@DSwu4RfYV4dJR^Q&LIS1MA*98Km~L z7%nu|SAB$eeEYb^%l_as!&G%#TwhmEL3z4W^;y9C8m8X7~4ht^w&9cLAa z>uM6z&D90aI=o-@mcDxjYZOp~++JeD$NQ}fbp7JNa#fe+s&;O<1afl|5!VdZEur@p zym&ZACOnxgyi6Vd`ZPa=!7D-c#hOZ8jGY$iEj@&=RkgPnrRg~~WxsJ3mKDA^9Lv~K zv@$G#nz`Mu367_JMRp-LHW@W2U|Klx$e05Ds9KkK)<3(udO8@+hOP1Co_N z)obXl;fwY<5zXu^CS4zLitx^6&v{q;#)}lynOI)Pv<~{%R8IF=AkETY1VkV z*{PS$#u!Zt_ur|tu7{14KtD9@LHfp{89|)t3zUfkico`z!T!{4*X>{;6G^f6Ec$&2 z7owD>^P9}j^$19JlzoL-f@;~6^eu+(XzEEM$aQkn3mRs#oR*})JS=M< zKTQJ>KOoZa;5OkmK+B9)m#7m6Lt{tJ?@8_l4wuy|v=5;7LPa|obVpgpNnZ0MB@HaV zZ&vGpW*eb=?G=%TF0!fvSu5X)svhk&1DfVj$n<5Jvm1NRS1r zlv5=BX20@)EY2b4PHG+7IjQ&L<%Mb^7WRrEik+W8;M=?^ZNOX@^M^^0W;-b6C3?o&?Egj35iqD=E@y9tP1z5Z}(lIkesDY@;S)9@CKNkyQ!#H z`b@EiK;E^n2mJp z@=O$!9g1TiwTKebu)g&vo0KaQq0pMg!f$$P-slQO8&K8IALmjXs$65T{BKV(hCmr?2+Zqq@J%Dn=Mk24`Kdx`U}d+RrpoJ|2{>!Il26#(7!dmz)hNV zdu;yL%lB9&`pfgL86Rem0#W$QrRtC~v_Z5O8VE1u76d5#(|)J5+1~qS>hbt@stX+^ zlujJe4>>9HUrhM-HTba87&1hGR-F+8Rt0MP7Zm)KzLp07{y~JR#tg^g005jg?h(hd J*{=F?_dkuTrxpMJ delta 2387 zcmZve2{aV?7sqF@Z^^zsvdd)O8T*>Cn?c!^rXf5#GnkB!$vzK*tl75`LiQmgDI_H! zBFa*>BIJ$#`}e&6|GeKh_x$epo%_A#-g|!MeC~8id?b^V8JL0&0H6T?xI_AkGML0^ zxyUyULtJ`QhL#{U6-rN%Wls36^RquiU9p-8KT{)(U$6?y^2NM0F@L%X*_q=#JFpu) zdsi48e#a$~KJW|fh1(UI`k>>=MScXM?u3w2ZaackjID;SySFK0j&_C+n*n*ofyO5!MTfK3Tr1g;lzo>X8a}k& zKw;ig)J>Mt?IMs#b>iD+KAaD7uDG};-#$<`Fjr;tymV1vKuFW9Xl{fv`|My3CsXCk zBa#ra`c*F?1z5}q!--f`Mw{<~ezijbIH{cw2(HKW6;~RW(TBw|00Dp+veA-T*Ei9% zFgAo)8vCW2TlC8^$G=`=`$CaLcmT@2o-SciPS@5uQK-m~%6~|xfkJJaI7}i`v}3;H zS8}X-Bw4ihwjf#~WD^(>;d@bPp5L1(**(E}L$*UJ`h1f8=yW;d>Gv7lT@>ckaQ4t-jX z7_MQ4$q`GrcZ~I^H!!sq4y1H00}(!w08gUY{H}1OWljgn?HOadc)H3>c3HSi$-T%b zac5vRom##n+yOrC9iEnwS8iNBrl*ziz-0|`<^^7E{?28F!__rk*9SsFdIW%19L-ZKQB09%zs*y zIqE^T$6Owu@!ih&?74u352dF0iY9Rw=qQWdScwV0=~Q4(cazyrptWFp+0w?dwT)5w zIey%fV$ySt<(>JXCF6%<&vQ+*dHrxqhK+vqc@-g*VfYP9{?6gS1E#>{A`9& zZ80epk6V%3E2s`FyYRTSTe)!IoY;&F@?c~_wI*shveLQqMnLrI{q(BIth*`Gqesi4 zs6=XK)~zLm)#6M1W|=Il4fmlrRyK<*$=%_h-0*3~lw%Fsn9UM6YJSEdk>)12Mz95l z_THtTrnPiiVU7Y|iDgnHgD@!py_n93xLobue-5OzG8}Au_IW7(2^WZsu&i&KG~m-& zkz{Q6ex@tx$S|i!wQ!?rd#9~Udn-E{Slm{~rw*%hN0J`dFo2=8_moA5}ZLx~!+eh2fHM;thII&GJ=IIOh%W4erMwumX zht{C2dClw97o)Ng8)zK)0WRAmlhh$FVEBSu#5f(Qmny)MZq{!t)~+k=ozn1SrHAOV zhJW-sF^NlF#ZvI1ycg>@M4Y``bdTpe-{tC4Bq-$!7w=2yVQ5*~9--(*MbTePUDNE! z(zla#`{|h-ON7@=I3e~t^ubPfl}m>oV@%JpPn1nOTAjKEjy-@U{b<@VvmbHiMef^K zLsE+)K*n}?-OC|{a_x}Db8?ctOL1%Kb0Jhrj2zC1*zLp5MqiqD`4uVxxJXSM`2ovI zlANr!O#LQ(n?Z8Mhe1E8!v)JxpTelN%(9z0Dwovs?DRT3K&t-McfziI8t^jF1f{+t zcHWBCgE_+Rv;^w!M=`EHiqF@^Yk#{JsJ3NQw)~09>(Jk#B@^VX$(D0#&RUi8p|DeC7r605n{@>KYDUcMGt{U-_8RGk z#Hb=SZgzj#Fd8k%*L|{`EC(4~hUkm6vkuBa!wAe^hC*cMPh-EG7H|H!*g*|G4*ThT z$gQFGtU|#SgVclY`PKq>*3S?1LoLDW^Gj;&tYSF@k3@Hc6i zC0IL0_B#@^pCP_-8D@TV{u7+T{abq3ZwFXcSz7jbZ?7LBH`IeTSrygFx;^JbnvxmR zRi(G92jAJE3X|la`n&Y_wTIM`bsEXIs^Ud}_JY=gr;M!cp^t%9Hrs(jBy=>+%2EKh zD$AyXL-5A)iWiVCv3uj!`hPeN6)qwuQn<4al3Q|G-K{4h+Ho)FcM|o>P21|-_U*8^|VKpN^$Dt4RENqW*Ru; z_O11rR{;)|tySMPoS&wKWxjdxn7PH-l`n^JCBe{>s~=K(Z|qZ^*Q>-sTWQ}-Oq_bg z8L`4@cO2JHs9ax8QP89D@4DY)YDkn4oP5)pN(F7m=GEGI;$^vgMk_NQhz)$YC=kXq zRZcer0t-~_KQJX&LU|M^$>{knaZg46k7fG55zP!(*Oed?KzaVk&;jKESpy0FK*>xk z2Ekukt|y1jl1<=m830fg8v!E6aS`f4v;=c4hQIXAT09^RFaZmelsYY%2Jmle{FSRL nmX$D~rL1B^#sJJz-_XL=@SoCuqr|C{q)1TL=A~LwJ9+vOACwZO diff --git a/src/.directory b/src/.directory new file mode 100644 index 00000000..a931c4fe --- /dev/null +++ b/src/.directory @@ -0,0 +1,4 @@ +[Dolphin] +Timestamp=2015,9,1,6,42,59 +Version=3 +ViewMode=1 diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index 3ff0c546..1c97bd25 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -23,6 +23,7 @@ import java.util.HashMap; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -488,4 +489,57 @@ public class AliasList implements Comparable { return getName().compareTo( otherAliasList.getName() ); } + + public Alias getAlias( String value, MetadataType type ) + { + switch( type ) + { + case ESN: + return getESNAlias( value ); + case FLEETSYNC: + return getFleetsyncAlias( value ); + case MDC1200: + return getMDC1200Alias( value ); + case MOBILE_ID: + return getMobileIDNumberAlias( value ); + case MPT1327: + return getMPT1327Alias( value ); + case SITE_ID: + return getSiteID( value ); + case STATUS: + if( value != null ) + { + try + { + return getStatus( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + case FROM_TALKGROUP: + case TO: + return getTalkgroupAlias( value ); + case UNIQUE_ID: + if( value != null ) + { + try + { + return getUniqueID( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + default: + break; + } + + return null; + } + } diff --git a/src/alias/Metadata.java b/src/alias/Metadata.java new file mode 100644 index 00000000..e1976183 --- /dev/null +++ b/src/alias/Metadata.java @@ -0,0 +1,102 @@ +package alias; + +public class Metadata +{ + private MetadataType mMetadataType; + private String mValue; + private Alias mValueAlias; + private boolean mTemporal; + + /** + * Constucts a metadata object with the temporal settings. Temporal + * indicates that this metadata will be removed at the end of a reset + * event, or call end event. + */ + public Metadata( MetadataType metadataType, String value, Alias alias, boolean temporal ) + { + mMetadataType = metadataType; + mValue = value; + mValueAlias = alias; + mTemporal = temporal; + } + + /** + * Constucts a metadata object with the temporal settings. Temporal + * indicates that this metadata will be removed at the end of a reset + * event, or call end event. + */ + public Metadata( MetadataType metadataType, String value, boolean temporal ) + { + this( metadataType, value, null, temporal ); + } + + /** + * Constucts a non-temporal metadata object + */ + public Metadata( MetadataType metadataType, String value, Alias alias ) + { + this( metadataType, value, alias, false ); + } + + /** + * Constucts a non-temporal metadata object + */ + public Metadata( MetadataType metadataType, String value ) + { + this( metadataType, value, null, false ); + } + + public MetadataType getMetadataType() + { + return mMetadataType; + } + + public String getKey() + { + return mMetadataType.getLabel(); + } + + public String getValue() + { + return mValue; + } + + public Alias getAlias() + { + return mValueAlias; + } + + public boolean hasAlias() + { + return mValueAlias != null; + } + + /** + * Indicates that this piece of metadata is only valid for the duration of + * a call event and should be removed during a reset + */ + public boolean isTemporal() + { + return mTemporal; + } + + public boolean isReset() + { + return false; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( mMetadataType.getLabel() ); + sb.append( ":" ); + sb.append( mValue ); + sb.append( " [" ); + sb.append( mMetadataType.name() ); + sb.append( "] temporal:" ); + sb.append( mTemporal ); + + return sb.toString(); + } +} diff --git a/src/alias/MetadataReset.java b/src/alias/MetadataReset.java new file mode 100644 index 00000000..8857ec4d --- /dev/null +++ b/src/alias/MetadataReset.java @@ -0,0 +1,14 @@ +package alias; + +public class MetadataReset extends Metadata +{ + public MetadataReset() + { + super( MetadataType.TEMPORAL_RESET, "reset", true ); + } + + public boolean isReset() + { + return true; + } +} diff --git a/src/alias/MetadataType.java b/src/alias/MetadataType.java new file mode 100644 index 00000000..e5426763 --- /dev/null +++ b/src/alias/MetadataType.java @@ -0,0 +1,38 @@ +package alias; + +public enum MetadataType +{ + CHANNEL( "Channel" ), + DETAILS( "Details" ), + ESN( "ESN" ), + FLEETSYNC( "Fleetsync" ), + FREQUENCY( "Frequency" ), + FROM_TALKGROUP( "From" ), + MDC1200( "MDC-1200" ), + MOBILE_ID( "Mobile ID" ), + MPT1327( "MPT-1327" ), + PROTOCOL( "Protocol" ), + SITE_ID( "Site ID" ), + STATUS( "Status" ), + TO( "To" ), + UNIQUE_ID( "Unique ID" ), + + TEMPORAL_RESET( "Reset" ); + + private String mLabel; + + private MetadataType( String label ) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return mLabel; + } +} diff --git a/src/alias/action/AliasActionManager.java b/src/alias/action/AliasActionManager.java index 87918777..007902e4 100644 --- a/src/alias/action/AliasActionManager.java +++ b/src/alias/action/AliasActionManager.java @@ -6,10 +6,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import message.Message; +import module.decode.p25.P25_LSMDecoder; import sample.Listener; import alias.Alias; import controller.ResourceManager; -import decode.p25.P25Decoder; /** * Manages all alias action events. Each received message is interrogated for diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java new file mode 100644 index 00000000..8197842a --- /dev/null +++ b/src/audio/AudioChannelPanel.java @@ -0,0 +1,356 @@ +package audio; + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import settings.ColorSetting; +import settings.Setting; +import settings.SettingChangeListener; +import settings.SettingsManager; +import settings.ColorSetting.ColorSettingName; +import alias.Alias; +import alias.Metadata; +import alias.MetadataType; +import audio.AudioEvent.Type; +import audio.metadata.AudioMetadata; + +public class AudioChannelPanel extends JPanel + implements Listener, SettingChangeListener +{ + private static final long serialVersionUID = 1L; + + private static final Logger mLog = LoggerFactory.getLogger( AudioChannelPanel.class ); + + private Font mFont = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); + private Color mLabelColor; + private Color mDetailsColor; + + private SettingsManager mSettingsManager; + private IAudioController mController; + private String mChannel; + + private JLabel mChannelLabel; + private JLabel mChannelName; + private JLabel mMutedLabel; + + private JLabel mFromLabel = new JLabel( "From:" ); + private JLabel mFrom = new JLabel( "" ); + private JLabel mFromAlias = new JLabel( "" ); + + private JLabel mToLabel = new JLabel( "To:" ); + private JLabel mTo = new JLabel( "" ); + private JLabel mToAlias = new JLabel( "" ); + + private boolean mConfigured = false; + + private BooleanControl mMuteControl; + private FloatControl mGainControl; + + public AudioChannelPanel( SettingsManager settingsManager, + IAudioController controller, + String channel ) + { + mSettingsManager = settingsManager; + mSettingsManager.addListener( this ); + + mLabelColor = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); + mDetailsColor = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); + + mController = controller; + mChannel = channel; + + try + { + mMuteControl = mController.getMuteControl( mChannel ); + } + catch ( AudioException e ) + { + mLog.error( "Couldn't obtain mute control from audio channel [" + + mChannel + "]", e ); + } + + try + { + mGainControl = mController.getGainControl( mChannel ); + } + catch ( AudioException e ) + { + mLog.error( "Couldn't obtain gain control from audio channel [" + + mChannel + "]", e ); + } + + try + { + mController.addAudioEventListener( mChannel, this ); + } + catch( AudioException e ) + { + mLog.error( "Couldn't register as audio event listener on channel [" + + mChannel + "]", e ); + } + + mController.setAudioMetadataListener( mChannel, new AudioMetadataProcessor() ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[right][][grow,fill]", "[grow,fill]0[]0[]" ) ); + setBackground( Color.BLACK ); + + addMouseListener( new MouseSelectionListener() ); + + mChannelLabel = new JLabel( "Channel:" ); + mChannelLabel.setFont( mFont ); + mChannelLabel.setForeground( mDetailsColor ); + + add( mChannelLabel ); + + mChannelName = new JLabel( mChannel ); + mChannelName.setFont( mFont ); + mChannelName.setForeground( mDetailsColor ); + + add( mChannelName ); + + mMutedLabel = new JLabel( "" ); + mMutedLabel.setFont( mFont ); + mMutedLabel.setForeground( Color.RED ); + + add( mMutedLabel, "wrap" ); + + mFromLabel.setFont( mFont ); + mFromLabel.setForeground( mLabelColor ); + add( mFromLabel ); + + mFrom.setFont( mFont ); + mFrom.setForeground( mLabelColor ); + add( mFrom ); + + mFromAlias.setFont( mFont ); + mFromAlias.setForeground( mLabelColor ); + add( mFromAlias,"wrap" ); + + mToLabel.setFont( mFont ); + mToLabel.setForeground( mLabelColor ); + add( mToLabel ); + + mTo.setFont( mFont ); + mTo.setForeground( mLabelColor ); + add( mTo ); + + mToAlias.setFont( mFont ); + mToAlias.setForeground( mLabelColor ); + add( mToAlias ); + } + + private void updateMuteState() + { + if( mMuteControl != null ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + mMutedLabel.setText( mMuteControl.getValue() ? "Muted" : "" ); + } + } ); + } + } + + @Override + public void receive( final AudioEvent audioEvent ) + { + if( audioEvent.getType() == Type.AUDIO_STOPPED ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + resetLabels(); + } + } ); + } + } + + /** + * Resets the from and to labels. Note: this does not happen on the swing + * event thread. Only invoke from the swing thread. + */ + private void resetLabels() + { + mFrom.setText( "" ); + updateAlias( mFromAlias, null ); + + mTo.setText( "" ); + updateAlias( mToAlias, null ); + + mConfigured = false; + } + + /** + * 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 updateAlias( JLabel label, Alias alias ) + { + if( alias != null ) + { + label.setText( alias.getName() ); + + String icon = alias.getIconName(); + + if( icon != null ) + { + label.setIcon( mSettingsManager.getImageIcon( icon, 12 ) ); + } + else + { + label.setIcon( null ); + } + } + else + { + label.setText( "" ); + label.setIcon( null ); + } + } + + public class MouseSelectionListener implements MouseListener + { + @Override + public void mouseClicked( MouseEvent event ) + { + JPopupMenu popup = new JPopupMenu(); + + if( mMuteControl != null ) + { + JMenuItem mute = new JMenuItem( mMuteControl.getValue() ? + "Unmute Channel: " + mChannel : "Mute Channel: " + mChannel ); + + mute.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mMuteControl.setValue( !mMuteControl.getValue() ); + updateMuteState(); + } + } ); + popup.add( mute ); + } + + popup.show( event.getComponent(), event.getX(), event.getY() ); + } + + @Override + public void mousePressed( MouseEvent e ) {} + @Override + public void mouseReleased( MouseEvent e ) {} + @Override + public void mouseEntered( MouseEvent e ) {} + @Override + public void mouseExited( MouseEvent e ) {} + } + + + /** + * Processes audio metadata to update this panel's display values + */ + public class AudioMetadataProcessor implements Listener + { + @Override + public void receive( AudioMetadata audioMetadata ) + { + if( !mConfigured || audioMetadata.isUpdated() ) + { + final Metadata from = audioMetadata.getMetadata( MetadataType.FROM_TALKGROUP ); + + final Metadata to = audioMetadata.getMetadata( MetadataType.TO ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + mFrom.setText( from.getValue() ); + updateAlias( mFromAlias, from.getAlias() ); + + mTo.setText( to.getValue() ); + updateAlias( mToAlias, to.getAlias() ); + } + } ); + + mConfigured = true; + } + } + } + + + @Override + public void settingChanged( Setting setting ) + { + if( setting instanceof ColorSetting ) + { + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) + { + case CHANNEL_STATE_LABEL_DECODER: + if( mFrom != null ) + { + mFrom.setForeground( mLabelColor ); + } + if( mFromAlias != null ) + { + mFromAlias.setForeground( mLabelColor ); + } + if( mTo != null ) + { + mTo.setForeground( mLabelColor ); + } + if( mToAlias != null ) + { + mToAlias.setForeground( mLabelColor ); + } + break; + case CHANNEL_STATE_LABEL_DETAILS: + if( mFromLabel != null ) + { + mFromLabel.setForeground( mDetailsColor ); + } + if( mToLabel != null ) + { + mToLabel.setForeground( mDetailsColor ); + } + break; + } + } + } + + @Override + public void settingDeleted( Setting setting ) {} +} diff --git a/src/audio/AudioEvent.java b/src/audio/AudioEvent.java new file mode 100644 index 00000000..e82bbf14 --- /dev/null +++ b/src/audio/AudioEvent.java @@ -0,0 +1,30 @@ +package audio; + +public class AudioEvent +{ + private Type mType; + private String mChannel; + + public AudioEvent( Type event, String channel ) + { + mType = event; + mChannel = channel; + } + + public Type getType() + { + return mType; + } + + public String getChannel() + { + return mChannel; + } + + public enum Type + { + AUDIO_STARTED, + AUDIO_STOPPED, + AUDIO_CONFIGURATION_CHANGED; + } +} diff --git a/src/audio/AudioException.java b/src/audio/AudioException.java new file mode 100644 index 00000000..365b605c --- /dev/null +++ b/src/audio/AudioException.java @@ -0,0 +1,16 @@ +package audio; + +public class AudioException extends Exception +{ + private static final long serialVersionUID = 1L; + + public AudioException( String description ) + { + super( description ); + } + + public AudioException( String description, Throwable throwable ) + { + super( description, throwable ); + } +} diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java new file mode 100644 index 00000000..09d7dc2f --- /dev/null +++ b/src/audio/AudioManager.java @@ -0,0 +1,379 @@ +package audio; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import audio.metadata.AudioMetadata; +import audio.output.AudioOutput; +import audio.output.MonoAudioOutput; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; + +public class AudioManager implements Listener, IAudioController +{ + private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); + + public static final int AUDIO_TIMEOUT = 2000; //2 seconds + public static final int NO_SOURCE = 0; + + private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); + private Map mChannelAssignments = new HashMap<>(); + private int mLowestPrioritySource; + + private List mAvailableAudioOutputs = new ArrayList<>(); + private Map mChannelMap = new HashMap<>(); + private List mChannelNames = new ArrayList<>(); + private AudioOutput mAudioOutput; + private boolean mMuted = false; + + private Listener mConfigurationChangeListener; + + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mProcessingTask; + + /** + * Handles all audio produced by the decoding channels and routes audio + * packets to any combination of outputs based on any alias audio routing + * options specified by the user. + */ + public AudioManager( ThreadPoolManager manager ) + { + mThreadPoolManager = manager; + + mAudioOutput = new MonoAudioOutput(); + mAvailableAudioOutputs.add( mAudioOutput ); + mChannelNames.add( mAudioOutput.getChannelName() ); + mChannelMap.put( mAudioOutput.getChannelName(), mAudioOutput ); + + mProcessingTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15, + TimeUnit.MILLISECONDS ); + } + + public void dispose() + { + if( mThreadPoolManager != null && mProcessingTask != null ) + { + mThreadPoolManager.cancel( mProcessingTask ); + } + + mAudioPacketQueue.clear(); + + mThreadPoolManager = null; + mProcessingTask = null; + + mAudioOutput.dispose(); + } + + @Override + public synchronized void receive( AudioPacket packet ) + { + mAudioPacketQueue.add( packet ); + } + + public class AudioPacketProcessor implements Runnable + { + @Override + public void run() + { + if( !mMuted ) + { + + } + removeCompletedChannelAssignments(); + + if( mAudioPacketQueue != null ) + { + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo( packets ); + + for( AudioPacket packet: packets ) + { + int source = packet.getMetadata().getSource(); + + /* Existing channel assignment */ + if( mChannelAssignments.containsKey( source ) ) + { + mChannelAssignments.get( source ).getAudioOutput() + .receive( packet ); + } + /* Start a new channel assignment */ + else if( !mAvailableAudioOutputs.isEmpty() ) + { + AudioOutput channel = mAvailableAudioOutputs.remove( 0 ); + + mChannelAssignments.put( packet.getMetadata().getSource(), + new AudioChannelAssignment( channel, packet.getMetadata().getPriority() ) ); + + updateLowestPriorityAssignment(); + + channel.receive( packet ); + } + /* Override the lowest priority assignment */ + else + { + if( mLowestPrioritySource > -1 ) + { + int priority = packet.getMetadata().getPriority(); + + if( packet.getMetadata().isSelected() || + priority < mChannelAssignments + .get( mLowestPrioritySource ).getPriority() ) + { + AudioChannelAssignment assignment = + mChannelAssignments.remove( mLowestPrioritySource ); + + if( assignment != null ) + { + assignment.setPriority( priority ); + + int overrideSource = packet.getMetadata().getSource(); + + mChannelAssignments.put( overrideSource, assignment ); + + mLowestPrioritySource = overrideSource; + + assignment.getAudioOutput().receive( packet ); + } + else + { + mLowestPrioritySource = -1; + } + } + } + } + } + } + } + } + + /** + * Checks each audio channel assignment and removes any completed channels. + */ + private void removeCompletedChannelAssignments() + { + if( !mChannelAssignments.isEmpty() ) + { + Iterator> it = + mChannelAssignments.entrySet().iterator(); + + Entry entry; + + boolean removed = false; + + while( it.hasNext() ) + { + entry = it.next(); + + if( entry.getValue().isComplete() ) + { + mAvailableAudioOutputs.add( entry.getValue().getAudioOutput() ); + entry.getValue().dispose(); + it.remove(); + removed = true; + } + } + + if( removed ) + { + updateLowestPriorityAssignment(); + } + } + } + + /** + * Identifies the lowest priority channel assignment ... the one that can + * be overridden with a higher priority call + */ + private void updateLowestPriorityAssignment() + { + int lowestPriority = -1; + + mLowestPrioritySource = -1; + + for( int source: mChannelAssignments.keySet() ) + { + AudioChannelAssignment assignment = mChannelAssignments.get( source ); + + if( assignment.getPriority() > lowestPriority ) + { + lowestPriority = assignment.getPriority(); + + mLowestPrioritySource = source; + } + } + } + + public class AudioChannelAssignment implements Listener + { + private static final long CALL_END_DELAY = 2000; //2 seconds + + private AudioOutput mAudioOutput; + private int mPriority; + private long mAudioStoppedTime = Long.MAX_VALUE; + + public AudioChannelAssignment( AudioOutput audioOutput, int priority ) + { + mAudioOutput = audioOutput; + mPriority = priority; + + mAudioOutput.addAudioEventListener( this ); + } + + public boolean isComplete() + { + return mAudioStoppedTime != Long.MAX_VALUE && + mAudioStoppedTime + CALL_END_DELAY < System.currentTimeMillis(); + } + + public void dispose() + { + mAudioOutput.removeAudioEventListener( this ); + mAudioOutput = null; + } + + public AudioOutput getAudioOutput() + { + return mAudioOutput; + } + + public int getPriority() + { + return mPriority; + } + + public void setPriority( int priority ) + { + mPriority = priority; + } + + @Override + public void receive( AudioEvent event ) + { + switch( event.getType() ) + { + case AUDIO_STARTED: + mAudioStoppedTime = Long.MAX_VALUE; + break; + case AUDIO_STOPPED: + mAudioStoppedTime = System.currentTimeMillis(); + break; + default: + break; + } + } + } + + @Override + public List getAudioChannels() + { + return mChannelNames; + } + + @Override + public BooleanControl getMuteControl( String channel ) + throws AudioException + { + if( mChannelMap.containsKey( channel ) ) + { + return mChannelMap.get( channel ).getMuteControl(); + } + + throw new AudioException( "Channel [" + channel + "] is not a valid channel" ); + } + + @Override + public boolean hasMuteControl( String channel ) + { + return mChannelMap.containsKey( channel ) && + mChannelMap.get( channel ) != null && + mChannelMap.get( channel ).getMuteControl() != null; + } + + @Override + public FloatControl getGainControl( String channel ) throws AudioException + { + if( mChannelMap.containsKey( channel ) ) + { + return mChannelMap.get( channel ).getGainControl(); + } + + throw new AudioException( "Channel [" + channel + "] is not a valid channel" ); + } + + @Override + public boolean hasGainControl( String channel ) + { + return mChannelMap.containsKey( channel ) && + mChannelMap.get( channel ) != null && + mChannelMap.get( channel ).getGainControl() != null; + } + + /** + * Sets the listener to receive audio events, specifically, audio + * configuration change events + */ + @Override + public void setConfigurationChangeListener( Listener listener ) + { + mConfigurationChangeListener = listener; + } + + @Override + public void addAudioEventListener( String channel, Listener listener ) + throws AudioException + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).addAudioEventListener( listener ); + } + else + { + throw new AudioException( "Channel [" + channel + "] is not a valid " + + "channel. Audio event listener was not registered" ); + } + } + + @Override + public void removeAudioEventListener( String channel, Listener listener ) + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).removeAudioEventListener( listener ); + } + } + + @Override + public void setAudioMetadataListener( String channel, + Listener listener ) + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).setAudioMetadataListener( listener ); + } + } + + @Override + public void removeAudioMetadataListener( String channel ) + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).removeAudioMetadataListener(); + } + } +} diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java new file mode 100644 index 00000000..b0e1a1d3 --- /dev/null +++ b/src/audio/AudioModule.java @@ -0,0 +1,191 @@ +package audio; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import alias.Metadata; +import audio.metadata.AudioMetadata; +import audio.metadata.IMetadataListener; +import audio.squelch.ISquelchStateListener; +import audio.squelch.SquelchState; +import controller.channel.ChannelEvent; +import controller.channel.IChannelEventListener; +import dsp.filter.Filters; +import dsp.filter.dc.AveragingDCRemovalFilter_RB; +import dsp.filter.dc.DCRemovalFilter_RB; +import dsp.filter.fir.real.RealFIRFilter_R_R; + +/** + * Provides packaging of demodulated audio sample buffers into audio packets for + * broadcast to registered audio packet listeners. Includes audio packet + * metadata in constructed audio packets. + * + * Incorporates audio squelch state listener to control if audio packets are + * broadcast or ignored. + */ +public class AudioModule extends Module implements IAudioPacketProvider, + IChannelEventListener, + IMetadataListener, + IRealBufferListener, + ISquelchStateListener, + Listener +{ + protected static final Logger mLog = LoggerFactory.getLogger( AudioModule.class ); + + /* Determines responsiveness of DC filter to frequency changes */ + private static final float DC_REMOVAL_RATIO = 0.000003f; + + /* Provides a unique identifier for this audio module instance to use as a + * source identifier for all audio packets */ + private static int UNIQUE_ID = 0; + private int mSourceID; + + private AudioMetadata mAudioMetadata; + + private ChannelEventListener mChannelEventListener = new ChannelEventListener(); + + private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); + private SquelchState mSquelchState = SquelchState.SQUELCH; + + private RealFIRFilter_R_R mAudioFilter = new RealFIRFilter_R_R( + Filters.FIR_BANDPASS_AUDIO_48KHZ.getCoefficients(), 2.0f ); + + private DCRemovalFilter_RB mDCRemovalFilter; + + private Listener mAudioPacketListener; + + private boolean mRemoveDC = false; + + public AudioModule() + { + this( false ); + } + + public AudioModule( boolean removeDC ) + { + mSourceID = ++UNIQUE_ID; + + mAudioMetadata = new AudioMetadata( mSourceID ); + + mRemoveDC = removeDC; + + if( mRemoveDC ) + { + mDCRemovalFilter = new AveragingDCRemovalFilter_RB( DC_REMOVAL_RATIO ); + mDCRemovalFilter.setListener( this ); + } + } + + @Override + public void dispose() + { + mSquelchStateListener = null; + mAudioPacketListener = null; + } + + @Override + public void init() + { + mAudioMetadata.reset(); + } + + /** + * Processes demodulated audio samples into audio packets with current audio + * metadata and sends to the registered listener + */ + @Override + public void receive( RealBuffer buffer ) + { + if( mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH ) + { + float[] audio = buffer.getSamples(); + + mAudioFilter.filter( audio ); + + AudioPacket packet = new AudioPacket( audio, mAudioMetadata.copyOf() ); + + mAudioPacketListener.receive( packet ); + } + } + + @Override + public Listener getRealBufferListener() + { + if( mRemoveDC ) + { + return mDCRemovalFilter; + } + + return this; + } + + @Override + public Listener getMetadataListener() + { + return mAudioMetadata; + } + + @Override + public void setAudioPacketListener( Listener listener ) + { + mAudioPacketListener = listener; + } + + @Override + public void removeAudioPacketListener() + { + mAudioPacketListener = null; + } + + @Override + public Listener getChannelEventListener() + { + return mChannelEventListener; + } + + @Override + public Listener getSquelchStateListener() + { + return mSquelchStateListener; + } + + /** + * Wrapper for channel event listener. Responds to channel state reset + * events to remove/cleanup current audio metadata + */ + public class ChannelEventListener implements Listener + { + @Override + public void receive( ChannelEvent event ) + { + switch( event.getEvent() ) + { + case CHANNEL_STATE_RESET: + mAudioMetadata.reset(); + break; + case CHANGE_SELECTED: + mAudioMetadata.setSelected( event.getChannel().isSelected() ); + break; + default: + break; + } + } + } + + /** + * Wrapper for squelch state listener + */ + public class SquelchStateListener implements Listener + { + @Override + public void receive( SquelchState state ) + { + mSquelchState = state; + } + } +} diff --git a/src/audio/AudioOutputImpl.java b/src/audio/AudioOutputImpl.java deleted file mode 100644 index 78d26ad0..00000000 --- a/src/audio/AudioOutputImpl.java +++ /dev/null @@ -1,124 +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 audio; - -import java.nio.ByteBuffer; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.SourceDataLine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.real.RealSampleListener; -import audio.inverted.AudioAdapter; -import audio.inverted.AudioType; -import audio.inverted.IAudioTypeListener; -import buffer.FloatSampleBufferAssembler; - -public class AudioOutputImpl implements RealSampleListener, - SquelchListener, IAudioTypeListener, IAudioOutput -{ - private final static Logger mLog = - LoggerFactory.getLogger( AudioOutputImpl.class ); - private static final int sAUDIO_BLOCK_SIZE = 24000; - - private static final AudioFormat sAUDIO_FORMAT = - new AudioFormat( 48000, //SampleRate - 16, //Sample Size - 1, //Channels - true, //Signed - false ); //Little Endian - - private AudioAdapter mAdapter = new AudioAdapter( 48000 ); - - private FloatSampleBufferAssembler mBuffer = - new FloatSampleBufferAssembler( sAUDIO_BLOCK_SIZE ); - private SourceDataLine mOutput; - - private boolean mEnabled = false; - private SquelchState mSquelchState = SquelchState.SQUELCH; - - public AudioOutputImpl( String channelName ) - { - try - { - mOutput = AudioSystem.getSourceDataLine( sAUDIO_FORMAT ); - mOutput.open( sAUDIO_FORMAT, (int)( sAUDIO_FORMAT.getSampleRate() ) ); - mOutput.start(); - } - catch ( LineUnavailableException e ) - { - mLog.error( "AudioOutput - couldn't open audio speakers " - + "for playback", e ); - } - } - - @Override - public void setSquelch( SquelchState state ) - { - mSquelchState = state; - } - - @Override - public void setAudioPlaybackEnabled( boolean enabled ) - { - mEnabled = enabled; - } - - @Override - public void dispose() - { - mOutput.stop(); - mOutput.close(); - mOutput = null; - } - - @Override - public void receive( float sample ) - { - float outputSample = mAdapter.get( sample ); - - if( mEnabled && mSquelchState == SquelchState.UNSQUELCH ) - { - mBuffer.put( outputSample ); - } - else - { - /* Use 0-valued samples when we're disabled or squelched */ - mBuffer.put( 0.0f ); - } - - if( !mBuffer.hasRemaining() ) - { - ByteBuffer buffer = mBuffer.get(); - - mOutput.write( buffer.array(), 0, buffer.array().length ); - - mBuffer.reset(); - } - } - - @Override - public void setAudioType( AudioType type ) - { - mAdapter.setAudioType( type ); - } -} diff --git a/src/audio/AudioOutputManager.java b/src/audio/AudioOutputManager.java deleted file mode 100644 index b85d7134..00000000 --- a/src/audio/AudioOutputManager.java +++ /dev/null @@ -1,25 +0,0 @@ -package audio; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AudioOutputManager -{ - private final static Logger mLog = - LoggerFactory.getLogger( AudioOutputManager.class ); - - /** - * Handles all audio produced by the decoding channels and routes audio - * packets to any combination of outputs based on any alias audio routing - * options specified by the user. - */ - public AudioOutputManager() - { - - } - - public static void main( String[] args ) - { - - } -} diff --git a/src/audio/AudioPacket.java b/src/audio/AudioPacket.java index 17de2efa..2989fa59 100644 --- a/src/audio/AudioPacket.java +++ b/src/audio/AudioPacket.java @@ -1,29 +1,34 @@ package audio; +import audio.metadata.AudioMetadata; + public class AudioPacket { - private String mSource; private Type mType; - private byte[] mAudioData; - private int mPriority; + private float[] mAudioData; + private AudioMetadata mAudioMetadata; - public AudioPacket( String source, Type type ) + public AudioPacket( Type type ) { - mSource = source; mType = type; } - public AudioPacket( String source, byte[] audio, int priority ) + public AudioPacket( float[] audio, AudioMetadata metadata ) { - this( source, Type.AUDIO ); + this( Type.AUDIO ); mAudioData = audio; - mPriority = priority; + mAudioMetadata = metadata; } - public String getSource() + public AudioMetadata getMetadata() { - return mSource; + return mAudioMetadata; + } + + public void setMetadata( AudioMetadata metadata ) + { + mAudioMetadata = metadata; } public Type getType() @@ -31,16 +36,11 @@ public class AudioPacket return mType; } - public byte[] getAudioData() + public float[] getAudioData() { return mAudioData; } - public int getPriority() - { - return mPriority; - } - public enum Type { AUDIO, diff --git a/src/audio/AudioPacketRateCounter.java b/src/audio/AudioPacketRateCounter.java new file mode 100644 index 00000000..cb888ab5 --- /dev/null +++ b/src/audio/AudioPacketRateCounter.java @@ -0,0 +1,56 @@ +package audio; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; + +public class AudioPacketRateCounter implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( AudioPacketRateCounter.class ); + + private Listener mListener; + private long mCount; + private long mStartTime = -1; + + private String mName; + + public AudioPacketRateCounter() + { + mName = "UNNAMED"; + } + + public AudioPacketRateCounter( String name ) + { + mName = name; + } + + @Override + public void receive( AudioPacket packet ) + { + if( mStartTime == -1 ) + { + mStartTime = System.currentTimeMillis(); + } + + mCount += packet.getAudioData().length; + + long elapsed = System.currentTimeMillis() - mStartTime; + + if( elapsed != 0 ) + { + mLog.debug( "[" + mName + "] Audio Rate: " + ( mCount / elapsed ) + " kHz" ); + } + + if( mListener != null ) + { + mListener.receive( packet ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/audio/AudioPanel.java b/src/audio/AudioPanel.java new file mode 100644 index 00000000..f62b733b --- /dev/null +++ b/src/audio/AudioPanel.java @@ -0,0 +1,120 @@ +package audio; + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.sound.sampled.BooleanControl; +import javax.swing.JButton; +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import settings.SettingsManager; + +public class AudioPanel extends JPanel implements Listener +{ + private static final long serialVersionUID = 1L; + + private static final Logger mLog = LoggerFactory.getLogger( AudioPanel.class ); + + private SettingsManager mSettingsManager; + private IAudioController mController; + private MuteButton mMuteButton; + + public AudioPanel( SettingsManager settingsManager, IAudioController controller ) + { + mSettingsManager = settingsManager; + mController = controller; + + mMuteButton = new MuteButton( mController ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") ); + setBackground( Color.BLACK ); + + add( mMuteButton, "span" ); + + for( String channel: mController.getAudioChannels() ) + { + add( new AudioChannelPanel( mSettingsManager, mController, channel ), "span" ); + } + } + + public int getPreferredHeight() + { + return mMuteButton.getHeight() + + ( mController.getAudioChannels().size() * 20 ); + } + + @Override + public void receive( AudioEvent event ) + { + if( event.getType() == AudioEvent.Type.AUDIO_CONFIGURATION_CHANGED ) + { + mLog.debug( "Audio configuration change detected" ); + } + } + + public class MuteButton extends JButton + { + private IAudioController mController; + + private boolean mMuted = false; + + public MuteButton( IAudioController controller ) + { + super( "Mute Audio" ); + + setBackground( Color.GREEN ); + + mController = controller; + + addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mMuted = !mMuted; + + for( String channel: mController.getAudioChannels() ) + { + try + { + BooleanControl control = mController.getMuteControl( channel ); + + if( control != null ) + { + control.setValue( mMuted ); + } + } + catch ( AudioException e1 ) + { + mLog.error( "Couldn't obtain mute control for " + + "channel [" + channel + "] from audio manager", e1 ); + } + } + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + MuteButton.this.setText( ( mMuted ? "Unmute Audio" : "Mute Audio" ) ); + MuteButton.this.setBackground( ( mMuted ? Color.RED : Color.GREEN ) ); + } + } ); + } + } ); + } + } +} diff --git a/src/audio/IAudioController.java b/src/audio/IAudioController.java new file mode 100644 index 00000000..eeb324e6 --- /dev/null +++ b/src/audio/IAudioController.java @@ -0,0 +1,36 @@ +package audio; + +import java.util.List; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; + +import sample.Listener; +import audio.metadata.AudioMetadata; + +/** + * Interface for controlling one or more audio channels + */ +public interface IAudioController +{ + public List getAudioChannels(); + + public BooleanControl getMuteControl( String channel ) throws AudioException; + + public boolean hasMuteControl( String channel ); + + public FloatControl getGainControl( String channel ) throws AudioException; + + public boolean hasGainControl( String channel ); + + public void addAudioEventListener( String channel, Listener listener ) + throws AudioException; + + public void removeAudioEventListener( String channel, Listener listener ); + + public void setAudioMetadataListener( String channel, Listener listener ); + + public void removeAudioMetadataListener( String channel ); + + public void setConfigurationChangeListener( Listener listener ); +} diff --git a/src/audio/IAudioOutput.java b/src/audio/IAudioOutput.java deleted file mode 100644 index 790f45ee..00000000 --- a/src/audio/IAudioOutput.java +++ /dev/null @@ -1,29 +0,0 @@ -package audio; - -import audio.SquelchListener.SquelchState; -import audio.inverted.AudioType; - -public interface IAudioOutput -{ - /** - * Squelch / Unsquelch state for this channel - */ - public abstract void setSquelch( SquelchState state ); - - /** - * Audio playback defines if this channel is currently playing on the - * system speakers. - */ - public abstract void setAudioPlaybackEnabled( boolean enabled ); - - /** - * Dispose of any system resources for this audio output - */ - public abstract void dispose(); - - /** - * Sets the audio to normal or inverted output - */ - public abstract void setAudioType( AudioType type ); - -} \ No newline at end of file diff --git a/src/audio/IAudioPacketProvider.java b/src/audio/IAudioPacketProvider.java new file mode 100644 index 00000000..47f4b0be --- /dev/null +++ b/src/audio/IAudioPacketProvider.java @@ -0,0 +1,9 @@ +package audio; + +import sample.Listener; + +public interface IAudioPacketProvider +{ + public void setAudioPacketListener( Listener listener ); + public void removeAudioPacketListener(); +} diff --git a/src/audio/inverted/AudioAdapter.java b/src/audio/inverted/AudioAdapter.java index 2d07a4e8..3c4b737b 100644 --- a/src/audio/inverted/AudioAdapter.java +++ b/src/audio/inverted/AudioAdapter.java @@ -88,7 +88,9 @@ public class AudioAdapter default: retVal = (float)( mPostInversionFilter.get( mAudioHighPassFilter.get( sample ) * - mSineWaveGenerator.nextFloat() ) ); + mSineWaveGenerator.getFloat() ) ); + + mSineWaveGenerator.rotate(); break; } diff --git a/src/audio/inverted/AudioInverter.java b/src/audio/inverted/AudioInverter.java index 26408556..c3b22236 100644 --- a/src/audio/inverted/AudioInverter.java +++ b/src/audio/inverted/AudioInverter.java @@ -38,7 +38,7 @@ public class AudioInverter implements RealSampleListener mSineWaveGenerator = new Oscillator( inversionFrequency, sampleRate ); mPostInversionLowPassFilter = new FloatFIRFilter( - Filters.FIRLP_55TAP_48000FS_3000FC.getCoefficients(), 1.04 ); + Filters.FIRLP_55TAP_48000FS_3000FC.getCoefficients(), 1.04f ); mPostInversionLowPassFilter.setListener( new FilteredSampleProcessor() ); } @@ -61,7 +61,9 @@ public class AudioInverter implements RealSampleListener if( mPostInversionLowPassFilter != null ) { mPostInversionLowPassFilter.receive( - sample * mSineWaveGenerator.nextFloat() ); + sample * mSineWaveGenerator.getFloat() ); + + mSineWaveGenerator.rotate(); } } diff --git a/src/audio/metadata/AudioMetadata.java b/src/audio/metadata/AudioMetadata.java new file mode 100644 index 00000000..a75da2fe --- /dev/null +++ b/src/audio/metadata/AudioMetadata.java @@ -0,0 +1,180 @@ +package audio.metadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import alias.Metadata; +import alias.MetadataType; + +public class AudioMetadata implements Listener +{ + protected static final Logger mLog = LoggerFactory.getLogger( AudioMetadata.class ); + + private int mSource; + private int mPriority = 99; + private boolean mSelected = false; + private List mMetadata = new ArrayList<>(); + private boolean mUpdated = false; + + public AudioMetadata( int source ) + { + mSource = source; + } + + public AudioMetadata copyOf() + { + AudioMetadata copy = new AudioMetadata( mSource ); + + copy.mPriority = mPriority; + copy.mSelected = mSelected; + copy.mMetadata.addAll( mMetadata ); + + if( mUpdated ) + { + copy.mUpdated = true; + + mUpdated = false; + } + + return copy; + } + + /** + * Removes any accumulated temporal metadata and returns to a default set. + */ + public void reset() + { + Iterator it = mMetadata.iterator(); + + while( it.hasNext() ) + { + if( it.next().isTemporal() ) + { + it.remove(); + } + } + + mUpdated = true; + } + + /** + * Indicates if this audio metadata contains updated information. The flag + * will only be set when new data is added and will be reset by a copyOf() + * method invocation. + */ + public boolean isUpdated() + { + return mUpdated; + } + + /** + * Adds the metadata. Any temporal metadata will be removed when the + * dispose() method is invoked. + */ + @Override + public void receive( Metadata metadata ) + { + if( metadata.isReset() ) + { + reset(); + } + else + { + mMetadata.add( metadata ); + + if( metadata.hasAlias() ) + { + //TODO: assign priority from alias here + } + } + + mUpdated = true; + } + + /** + * Returns the complete set of metadata + */ + public List getMetadata() + { + return mMetadata; + } + + /** + * Returns the first metadata object matching the type, or null if the + * metadata type is not found. + */ + public Metadata getMetadata( MetadataType type ) + { + for( Metadata metadata: mMetadata ) + { + if( metadata.getMetadataType() == type ) + { + return metadata; + } + } + + return null; + } + + /** + * Source channel ID + */ + public void setSource( int source ) + { + mSource = source; + + mUpdated = true; + } + + public int getSource() + { + return mSource; + } + + /** + * Sets the priority of this audio packet in the range of 1 - Max Integer value + * + * @param priority (1 - Max Int) + */ + public void setPriority( int priority ) + { + assert( 1 <= priority && priority <= Integer.MAX_VALUE ); + + mPriority = priority; + + mUpdated = true; + } + + /** + * Returns the priority of this audio packet or a priority of -1 if the + * audio source is currently selected, so that the priority of this packet + * is higher (lower?) than any other audio source + * + * @return - priority of audio packet where lower numbers are higher priority + */ + public int getPriority() + { + return mSelected ? -1 : mPriority; + } + + /** + * Selected audio channel. A selected channel will override the priority + * level so that the audio can be listened to immediately. + */ + public void setSelected( boolean selected ) + { + mSelected = selected; + + mUpdated = true; + } + + public boolean isSelected() + { + return mSelected; + } +} diff --git a/src/audio/metadata/IMetadataListener.java b/src/audio/metadata/IMetadataListener.java new file mode 100644 index 00000000..a4672515 --- /dev/null +++ b/src/audio/metadata/IMetadataListener.java @@ -0,0 +1,9 @@ +package audio.metadata; + +import sample.Listener; +import alias.Metadata; + +public interface IMetadataListener +{ + public Listener getMetadataListener(); +} diff --git a/src/audio/metadata/IMetadataProvider.java b/src/audio/metadata/IMetadataProvider.java new file mode 100644 index 00000000..afb2ebdc --- /dev/null +++ b/src/audio/metadata/IMetadataProvider.java @@ -0,0 +1,10 @@ +package audio.metadata; + +import sample.Listener; +import alias.Metadata; + +public interface IMetadataProvider +{ + public void setMetadataListener( Listener listener ); + public void removeMetadataListener(); +} diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java new file mode 100644 index 00000000..9b81abf3 --- /dev/null +++ b/src/audio/output/AudioOutput.java @@ -0,0 +1,84 @@ +package audio.output; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; +import audio.AudioEvent; +import audio.AudioPacket; +import audio.metadata.AudioMetadata; + +public abstract class AudioOutput implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); + + private AtomicBoolean mSquelched = new AtomicBoolean( false ); + private Listener mAudioMetadataListener; + protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); + + public void dispose() + { + mAudioEventBroadcaster.dispose(); + mAudioEventBroadcaster = null; + mAudioMetadataListener = null; + } + + public abstract String getChannelName(); + + public abstract BooleanControl getMuteControl(); + public abstract boolean hasMuteControl(); + + public abstract FloatControl getGainControl(); + public abstract boolean hasGainControl(); + + protected void broadcast( AudioMetadata metadata ) + { + if( mAudioMetadataListener != null ) + { + mAudioMetadataListener.receive( metadata ); + } + } + + public void setSquelched( boolean squelched ) + { + mSquelched.set( squelched ); + } + + public boolean isSquelched() + { + return mSquelched.get(); + } + + /** + * Registers a single listener to receive audio start and audio stop events + */ + public void addAudioEventListener( Listener listener ) + { + mAudioEventBroadcaster.addListener( listener ); + } + + public void removeAudioEventListener( Listener listener ) + { + mAudioEventBroadcaster.removeListener( listener ); + } + + /** + * Registers a single listener to receive the audio metadata from each + * audio packet + */ + public void setAudioMetadataListener( Listener listener ) + { + mAudioMetadataListener = listener; + } + + public void removeAudioMetadataListener() + { + mAudioMetadataListener = null; + } +} diff --git a/src/audio/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java similarity index 60% rename from src/audio/MonoAudioOutput.java rename to src/audio/output/MonoAudioOutput.java index 2148509f..eb16ddb3 100644 --- a/src/audio/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -1,5 +1,8 @@ -package audio; +package audio.output; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; @@ -9,20 +12,25 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; +import audio.AudioEvent; +import audio.AudioFormats; +import audio.AudioPacket; import audio.AudioPacket.Type; +import controller.NamingThreadFactory; /** * Audio output with automatic flow control based on the availability of * audio data packets. */ -public class MonoAudioOutput implements Listener +public class MonoAudioOutput extends AudioOutput { private final static Logger mLog = LoggerFactory.getLogger( MonoAudioOutput.class ); @@ -43,15 +51,24 @@ public class MonoAudioOutput implements Listener private ScheduledExecutorService mExecutorService; private SourceDataLine mOutput; + private FloatControl mGainControl; + private BooleanControl mMuteControl; private boolean mCanProcessAudio; - private AtomicBoolean mMuted = new AtomicBoolean( true ); - private AtomicBoolean mSquelched = new AtomicBoolean( false ); - + private long mLastBufferReceived; + + private AudioEvent mAudioStartEvent; + private AudioEvent mAudioStopEvent; + public MonoAudioOutput() { - try + super(); + + mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, getChannelName() ); + mAudioStopEvent = new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, getChannelName() ); + + try { mOutput = AudioSystem.getSourceDataLine( AudioFormats.PCM_SIGNED_48KHZ_16BITS ); @@ -62,7 +79,14 @@ public class MonoAudioOutput implements Listener mCanProcessAudio = true; - mExecutorService = Executors.newSingleThreadScheduledExecutor(); + mGainControl = (FloatControl)mOutput.getControl( + FloatControl.Type.MASTER_GAIN ); + + mMuteControl = (BooleanControl)mOutput.getControl( + BooleanControl.Type.MUTE ); + + mExecutorService = Executors.newSingleThreadScheduledExecutor( + new NamingThreadFactory( "audio (mono) output" ) ); /* Run the queue processor task every 40 milliseconds */ mExecutorService.scheduleAtFixedRate( new QueueProcessor(), @@ -76,19 +100,24 @@ public class MonoAudioOutput implements Listener } } - public void setMuted( boolean muted ) + @Override + public String getChannelName() { - mMuted.set( muted ); + return "Mono"; } - public void setSquelched( boolean squelched ) + /** + * Timestamp when most recent buffer was received + */ + public long getLastBufferReceived() { - mSquelched.set( squelched ); + return mLastBufferReceived; } - - + public void dispose() { + super.dispose(); + mCanProcessAudio = false; mBuffer.clear(); @@ -109,10 +138,13 @@ public class MonoAudioOutput implements Listener { if( mCanProcessAudio ) { + mLastBufferReceived = System.currentTimeMillis(); + mBuffer.add( packet ); } } + public class QueueProcessor implements Runnable { private AtomicBoolean mProcessing = new AtomicBoolean(); @@ -131,23 +163,39 @@ public class MonoAudioOutput implements Listener List packets = new ArrayList(); mBuffer.drainTo( packets ); - + if( packets.size() > 0 ) { for( AudioPacket packet: packets ) { if( mCanProcessAudio ) { - if( mMuted.get() || mSquelched.get() ) + if( isSquelched() ) { checkStop(); } else if( packet.getType() == Type.AUDIO ) { - mOutput.write( packet.getAudioData(), 0, - packet.getAudioData().length ); + float[] samples = packet.getAudioData(); + + /* Little-endian byte buffer */ + ByteBuffer buffer = + ByteBuffer.allocate( samples.length * 2 ) + .order( ByteOrder.LITTLE_ENDIAN ); + + ShortBuffer shortBuffer = buffer.asShortBuffer(); + + for( float sample: samples ) + { + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); + } + + mOutput.write( buffer.array(), 0, + buffer.array().length ); checkStart(); + + broadcast( packet.getMetadata() ); } } } @@ -163,21 +211,50 @@ public class MonoAudioOutput implements Listener private void checkStart() { - if( mCanProcessAudio && !mMuted.get() && !mSquelched.get() && + if( mCanProcessAudio && !isSquelched() && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); + + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - ( mOutput.available() >= STOP_THRESHOLD || mMuted.get() || mSquelched.get() ) ) + ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) { mOutput.drain(); mOutput.stop(); + + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } + + + @Override + public BooleanControl getMuteControl() + { + return mMuteControl; + } + + @Override + public boolean hasMuteControl() + { + return mMuteControl != null; + } + + @Override + public FloatControl getGainControl() + { + return mGainControl; + } + + @Override + public boolean hasGainControl() + { + return mGainControl != null; + } } diff --git a/src/audio/squelch/ISquelchStateListener.java b/src/audio/squelch/ISquelchStateListener.java new file mode 100644 index 00000000..d5738f51 --- /dev/null +++ b/src/audio/squelch/ISquelchStateListener.java @@ -0,0 +1,8 @@ +package audio.squelch; + +import sample.Listener; + +public interface ISquelchStateListener +{ + public Listener getSquelchStateListener(); +} diff --git a/src/audio/squelch/ISquelchStateProvider.java b/src/audio/squelch/ISquelchStateProvider.java new file mode 100644 index 00000000..93b435f2 --- /dev/null +++ b/src/audio/squelch/ISquelchStateProvider.java @@ -0,0 +1,9 @@ +package audio.squelch; + +import sample.Listener; + +public interface ISquelchStateProvider +{ + public void setSquelchStateListener( Listener listener ); + public void removeSquelchStateListener(); +} diff --git a/src/audio/squelch/ManualSquelchController.java b/src/audio/squelch/ManualSquelchController.java new file mode 100644 index 00000000..cce0ed53 --- /dev/null +++ b/src/audio/squelch/ManualSquelchController.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package audio.squelch; + + +/** + * Provides squelch control based on presense of decoded audio samples. + * + * NOTE: this class is not yet fully implemented with audio sample processing + * and currently only provides user driven squelch control + */ +public class ManualSquelchController extends SquelchController +{ + public ManualSquelchController() + { + mSquelchMode = SquelchMode.NONE; + } + + @Override + public void setSquelchMode( SquelchMode mode ) + { + //TODO: add functionality + } +} diff --git a/src/audio/squelch/SquelchController.java b/src/audio/squelch/SquelchController.java new file mode 100644 index 00000000..af4eaec9 --- /dev/null +++ b/src/audio/squelch/SquelchController.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package audio.squelch; + +import sample.Broadcaster; +import sample.Listener; +import sample.real.RealBuffer; +import sample.real.RealSampleListener; +import sample.real.RealToRealBufferAssembler; + +/** + * Squelch control - receives single audio samples or real audio buffers and + * adjusts squelch state either based on the audio samples or based on external + * control inputs. + * + * Assembles received single audio samples into real sample buffers for + * consumption by an external audio processor, like the AudioPacketProcessor + * class. + */ +public abstract class SquelchController + implements Listener, RealSampleListener +{ + private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); + private Listener mAudioListener; + protected SquelchState mSquelchState = SquelchState.UNSQUELCH; + protected SquelchMode mSquelchMode = SquelchMode.MANUAL; + + private RealToRealBufferAssembler mSampleAssembler = new RealToRealBufferAssembler( 512 ); + + public SquelchController() + { + mSampleAssembler.setListener( this ); + } + + @Override + public void receive( float sample ) + { + mSampleAssembler.receive( sample ); + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mAudioListener != null ) + { + mAudioListener.receive( buffer ); + } + } + + public abstract void setSquelchMode( SquelchMode mode ); + + public SquelchMode getSquelchMode() + { + return mSquelchMode; + } + + public void setSquelchState( SquelchState state ) + { + mSquelchState = state; + } + + public SquelchState getSquelchState() + { + return mSquelchState; + } + + public boolean isSquelched() + { + switch( mSquelchMode ) + { + case AUTOMATIC: + case MANUAL: + return mSquelchState == SquelchState.SQUELCH; + case NONE: + return false; + default: + return false; + } + } + + public void addSquelchStateListener( Listener listener ) + { + if( listener != null ) + { + mSquelchStateBroadcaster.addListener( listener ); + + /* Send current squelch state to the listener */ + listener.receive( mSquelchState ); + } + } + + public void removeSquelchStateListener( Listener listener ) + { + mSquelchStateBroadcaster.removeListener( listener ); + } + + public void setAudioListener( Listener listener ) + { + mAudioListener = listener; + } + + public void removeAudioListener( Listener listener ) + { + mAudioListener = null; + } +} diff --git a/src/audio/squelch/SquelchMode.java b/src/audio/squelch/SquelchMode.java new file mode 100644 index 00000000..d5f09b9d --- /dev/null +++ b/src/audio/squelch/SquelchMode.java @@ -0,0 +1,25 @@ +package audio.squelch; + +/** + * Squelch state for a decoding channel. The state determines the handling of + * any audio packets processed by the channel state. + */ +public enum SquelchMode +{ + /** + * Automatic: decoded message activity dictates squelch state and determines + * if audio is passed + */ + AUTOMATIC, + + /** + * Manually applied squelch noise level threshold determines if audio is + * passed. + */ + MANUAL, + + /** + * No Squelch - no squelch is applied and all audio is passed + */ + NONE; +} diff --git a/src/audio/squelch/SquelchState.java b/src/audio/squelch/SquelchState.java new file mode 100644 index 00000000..1093eae8 --- /dev/null +++ b/src/audio/squelch/SquelchState.java @@ -0,0 +1,7 @@ +package audio.squelch; + +public enum SquelchState +{ + SQUELCH, + UNSQUELCH; +} diff --git a/src/decode/tait/Tait1200ChannelState.java b/src/audio/squelch/TrunkingSquelchController.java similarity index 50% rename from src/decode/tait/Tait1200ChannelState.java rename to src/audio/squelch/TrunkingSquelchController.java index 5f85b17d..18d50341 100644 --- a/src/decode/tait/Tait1200ChannelState.java +++ b/src/audio/squelch/TrunkingSquelchController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,49 +15,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; -import message.Message; -import controller.state.AuxChannelState; -import controller.state.ChannelState; +package audio.squelch; -public class Tait1200ChannelState extends AuxChannelState +public class TrunkingSquelchController extends SquelchController { - private Tait1200ActivitySummary mActivitySummary; - - public Tait1200ChannelState( ChannelState parentState ) + private boolean mAllowSquelchOverride = true; + + public TrunkingSquelchController() { - super( parentState ); + } + + /** + * Provides control over audio stream as directed by an external trunking + * channel state. + * + * Override automatic squelch control by setting the squelch mode to none. + * + * Disable automatic control override by setting allowManualOverride to false; + * + * Note: this control does not respond to squelch mode manual. + */ + public TrunkingSquelchController( boolean allowSquelchOverride ) + { + mAllowSquelchOverride = allowSquelchOverride; - mActivitySummary = - new Tait1200ActivitySummary( parentState.getAliasList() ); + mSquelchMode = SquelchMode.AUTOMATIC; } - + @Override - public void reset() + public void setSquelchMode( SquelchMode mode ) { - } - - @Override - public void fade() - { - } - - @Override - public void receive( Message message ) - { - if( message instanceof Tait1200GPSMessage ) + if( mode == SquelchMode.AUTOMATIC || + ( mode == SquelchMode.NONE && mAllowSquelchOverride ) ) { - mActivitySummary.receive( message ); - -// mParentChannelState.receiveCallEvent( -// Tait1200CallEvent.getLJ1200Event( (Tait1200Message)message ) ); + mSquelchMode = mode; } - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } + } } diff --git a/src/buffer/ComplexByteBuffer.java b/src/buffer/ComplexByteBuffer.java index 0fc4e95b..1abb25dc 100644 --- a/src/buffer/ComplexByteBuffer.java +++ b/src/buffer/ComplexByteBuffer.java @@ -20,14 +20,14 @@ package buffer; import java.nio.ByteBuffer; import java.util.Enumeration; -import sample.complex.ComplexSample; +import sample.complex.Complex; /** * Provides a complex sample enumeration wrapper around a byte array containing * interleaved byte-length I & Q sample values in the range of 0 - 255, converting * those samples into complex samples with value range of -128 to 0 to 127. */ -public class ComplexByteBuffer implements Enumeration +public class ComplexByteBuffer implements Enumeration { private static float[] mValues; @@ -81,11 +81,11 @@ public class ComplexByteBuffer implements Enumeration * method to ensure you do not read past the end of the buffer. */ @Override - public ComplexSample nextElement() + public Complex nextElement() { // return new ComplexSample( mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ], // mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ] ); - return new ComplexSample( mValues[ ( mBytes.get() & 0xFF ) ], + return new Complex( mValues[ ( mBytes.get() & 0xFF ) ], mValues[ ( mBytes.get() & 0xFF ) ] ); } } diff --git a/src/buffer/ComplexCircularBuffer.java b/src/buffer/ComplexCircularBuffer.java index d782ea31..f375a658 100644 --- a/src/buffer/ComplexCircularBuffer.java +++ b/src/buffer/ComplexCircularBuffer.java @@ -19,7 +19,7 @@ package buffer; import java.util.Arrays; -import sample.complex.ComplexSample; +import sample.complex.Complex; /** * Circular sample buffer - allocates a buffer and stores samples in a circular @@ -30,14 +30,14 @@ import sample.complex.ComplexSample; */ public class ComplexCircularBuffer { - ComplexSample[] mBuffer; + Complex[] mBuffer; int mBufferPointer = 0; public ComplexCircularBuffer( int size ) { - mBuffer = new ComplexSample[ size ]; + mBuffer = new Complex[ size ]; - Arrays.fill( mBuffer, new ComplexSample( 1.0f, 1.0f ) ); + Arrays.fill( mBuffer, new Complex( 1.0f, 1.0f ) ); } /** @@ -46,16 +46,16 @@ public class ComplexCircularBuffer * @param sample * @return */ - public ComplexSample get( ComplexSample sample ) + public Complex get( Complex sample ) { - ComplexSample retVal = mBuffer[ mBufferPointer ]; + Complex retVal = mBuffer[ mBufferPointer ]; put( sample ); return retVal; } - public void put( ComplexSample sample ) + public void put( Complex sample ) { mBuffer[ mBufferPointer ] = sample; @@ -67,9 +67,9 @@ public class ComplexCircularBuffer } } - public ComplexSample[] get( int count ) + public Complex[] get( int count ) { - ComplexSample[] samples = new ComplexSample[ count ]; + Complex[] samples = new Complex[ count ]; int tempCounter = mBufferPointer; diff --git a/src/buffer/ComplexSampleBufferAssembler.java b/src/buffer/ComplexSampleBufferAssembler.java index 8931d4d7..935696f1 100644 --- a/src/buffer/ComplexSampleBufferAssembler.java +++ b/src/buffer/ComplexSampleBufferAssembler.java @@ -21,7 +21,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexSampleBufferAssembler { @@ -41,7 +41,7 @@ public class ComplexSampleBufferAssembler * Load the sample's bytes, left then right, little endian, into the * backing byte array */ - public void put( ComplexSample sample ) + public void put( Complex sample ) { mByteBuffer.putShort( (short)( sample.left() * Short.MAX_VALUE ) ); mByteBuffer.putShort( (short)( sample.right() * Short.MAX_VALUE ) ); diff --git a/src/buffer/ComplexTappedCircularBuffer.java b/src/buffer/ComplexTappedCircularBuffer.java index 1fad24ef..214738d3 100644 --- a/src/buffer/ComplexTappedCircularBuffer.java +++ b/src/buffer/ComplexTappedCircularBuffer.java @@ -17,7 +17,7 @@ ******************************************************************************/ package buffer; -import sample.complex.ComplexSample; +import sample.complex.Complex; /** * Implements a FIFO circular buffer for complex samples that provides access @@ -26,17 +26,17 @@ import sample.complex.ComplexSample; */ public class ComplexTappedCircularBuffer { - private ComplexSample[] mBuffer; + private Complex[] mBuffer; private int mBufferPointer = 0; public ComplexTappedCircularBuffer( int length ) { - mBuffer = new ComplexSample[ length ]; + mBuffer = new Complex[ length ]; //Pre-load the buffer with 0-valued samples for( int x = 0; x < length; x++ ) { - mBuffer[ x ] = new ComplexSample( 0, 0 ); + mBuffer[ x ] = new Complex( 0, 0 ); } } @@ -44,7 +44,7 @@ public class ComplexTappedCircularBuffer * Adds the sample to the buffer and removes the oldest sample * @param sample */ - public void add( ComplexSample sample ) + public void add( Complex sample ) { mBuffer[ mBufferPointer++ ] = sample; @@ -68,7 +68,7 @@ public class ComplexTappedCircularBuffer * @throws ArrayIndexOutOfBoundsException if you attempt to access a sample * outside of the buffer size */ - public ComplexSample get( int index ) + public Complex get( int index ) { /* Since the samples are stored in a forward fashion, we have to access * the samples in reverse to comply with the access contract, where diff --git a/src/buffer/ShortComplexByteBuffer.java b/src/buffer/ShortComplexByteBuffer.java index 2a613797..2aff0558 100644 --- a/src/buffer/ShortComplexByteBuffer.java +++ b/src/buffer/ShortComplexByteBuffer.java @@ -21,9 +21,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Enumeration; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ShortComplexByteBuffer implements Enumeration +public class ShortComplexByteBuffer implements Enumeration { private ByteBuffer mByteBuffer; @@ -45,9 +45,9 @@ public class ShortComplexByteBuffer implements Enumeration } @Override - public ComplexSample nextElement() + public Complex nextElement() { - return new ComplexSample( (float)mByteBuffer.getShort(), + return new Complex( (float)mByteBuffer.getShort(), (float)mByteBuffer.getShort() ); } } diff --git a/src/buffer/StereoByteBuffer.java b/src/buffer/StereoByteBuffer.java index 1d7688da..6e4e6db4 100644 --- a/src/buffer/StereoByteBuffer.java +++ b/src/buffer/StereoByteBuffer.java @@ -21,10 +21,10 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Enumeration; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.mixer.MixerChannel; -public class StereoByteBuffer implements Enumeration +public class StereoByteBuffer implements Enumeration { private ByteBuffer mByteBuffer; @@ -51,9 +51,9 @@ public class StereoByteBuffer implements Enumeration } @Override - public ComplexSample nextElement() + public Complex nextElement() { - return new ComplexSample( (float)mByteBuffer.getShort(), + return new Complex( (float)mByteBuffer.getShort(), (float)mByteBuffer.getShort() ); } } diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java index bc24d0c2..8b20b272 100644 --- a/src/controller/ConfigurationControllerModel.java +++ b/src/controller/ConfigurationControllerModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -29,6 +29,8 @@ import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; import map.IconManager; +import module.decode.DecoderType; +import module.decode.config.DecodeConfigFactory; import playlist.PlaylistNode; import source.config.SourceConfigTuner; import source.recording.RecordingGroupNode; @@ -37,14 +39,11 @@ import source.tuner.TunerGroupNode; import source.tuner.TunerNode; import source.tuner.TunerSelectionListener; import controller.channel.Channel; -import controller.channel.ChannelEventListener; import controller.channel.ChannelNode; import controller.site.Site; import controller.system.System; import controller.system.SystemListNode; import controller.system.SystemNode; -import decode.DecoderType; -import decode.config.DecodeConfigFactory; public class ConfigurationControllerModel extends DefaultTreeModel { @@ -54,12 +53,8 @@ public class ConfigurationControllerModel extends DefaultTreeModel private ResourceManager mResourceManager; - private ArrayList mChannelConfigChangeListeners = - new ArrayList(); - private ArrayList mTunerSelectionListeners = new ArrayList(); - private PlaylistNode mPlaylistNode; private TunerGroupNode mTunerGroupNode; diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index a68cb667..f7dd9f19 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -21,7 +21,6 @@ import java.awt.Color; import java.awt.Component; import java.awt.Dimension; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -30,23 +29,24 @@ import javax.swing.event.ChangeListener; import map.MapPanel; import message.Message; +import module.decode.event.CallEventPanel; +import module.decode.event.MessageActivityPanel; +import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; import sample.Listener; import spectrum.ChannelSpectrumPanel; +import audio.AudioPanel; import com.jidesoft.swing.JideSplitPane; import com.jidesoft.swing.JideTabbedPane; -import controller.activity.CallEventPanel; -import controller.activity.MessageActivityPanel; import controller.channel.ChannelManager; -import controller.state.ChannelStateList; public class ControllerPanel extends JPanel { private static final long serialVersionUID = 1L; - private ChannelStateList mChannelStateList; + private ChannelList mChannelStateList; private CallEventPanel mCallEventPanel = new CallEventPanel(); @@ -135,7 +135,8 @@ public class ControllerPanel extends JPanel /* Channel state list */ - mChannelStateList = new ChannelStateList( mResourceManager.getSettingsManager() ); + mChannelStateList = new ChannelList( mResourceManager.getPlaylistManager(), + mResourceManager.getSettingsManager() ); /* Register each of the components to receive channel events when the * channels are selected or change */ @@ -147,12 +148,20 @@ public class ControllerPanel extends JPanel JScrollPane channelStateListScroll = new JScrollPane(); channelStateListScroll.getViewport().setView( mChannelStateList ); - channelStateListScroll.setPreferredSize( new Dimension( 150, - (int)channelStateListScroll.getPreferredSize().getHeight() ) ); + channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) ); + AudioPanel audioPanel = new AudioPanel( mResourceManager.getSettingsManager(), + mResourceManager.getAudioManager() ); + audioPanel.setPreferredSize( new Dimension( 200, audioPanel.getPreferredHeight() ) ); + + JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); + audioChannelListSplit.setDividerSize( 5 ); + audioChannelListSplit.add( audioPanel ); + audioChannelListSplit.add( channelStateListScroll ); + JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); channelSplit.setDividerSize( 5 ); - channelSplit.add( channelStateListScroll ); + channelSplit.add( audioChannelListSplit ); channelSplit.add( mTabbedPane ); add( channelSplit ); diff --git a/src/controller/NamingThreadFactory.java b/src/controller/NamingThreadFactory.java new file mode 100644 index 00000000..ae3302b1 --- /dev/null +++ b/src/controller/NamingThreadFactory.java @@ -0,0 +1,48 @@ +package controller; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Thread factory that applies custom names to threads + */ +public class NamingThreadFactory implements ThreadFactory +{ + static final AtomicInteger poolNumber = new AtomicInteger(1); + + final ThreadGroup mThreadGroup; + + final AtomicInteger mThreadNumber = new AtomicInteger(1); + + final String mNamePrefix; + + public NamingThreadFactory( String prefix ) + { + SecurityManager s = System.getSecurityManager(); + + mThreadGroup = (s != null)? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + + mNamePrefix = prefix + " pool-" + + poolNumber.getAndIncrement() + + "-thread-"; + } + + public Thread newThread( Runnable runnable ) + { + Thread thread = new Thread(mThreadGroup, runnable, + mNamePrefix + mThreadNumber.getAndIncrement(), 0 ); + + if( thread.isDaemon() ) + { + thread.setDaemon( false ); + } + + if( thread.getPriority() != Thread.NORM_PRIORITY ) + { + thread.setPriority( Thread.NORM_PRIORITY ); + } + + return thread; + } +} \ No newline at end of file diff --git a/src/controller/ResourceManager.java b/src/controller/ResourceManager.java index ee5c1b6f..15e14016 100644 --- a/src/controller/ResourceManager.java +++ b/src/controller/ResourceManager.java @@ -17,7 +17,6 @@ ******************************************************************************/ package controller; -import alias.action.AliasActionManager; import map.MapService; import playlist.PlaylistManager; import record.RecorderManager; @@ -25,6 +24,7 @@ import settings.SettingsManager; import source.SourceManager; import source.recording.RecordingSourceManager; import source.tuner.TunerManager; +import audio.AudioManager; import controller.channel.ChannelManager; import eventlog.EventLogManager; @@ -33,6 +33,7 @@ import eventlog.EventLogManager; */ public class ResourceManager { + private AudioManager mAudioManager; private ChannelManager mChannelManager; private EventLogManager mEventLogManager; private PlaylistManager mPlaylistManager; @@ -67,13 +68,24 @@ public class ResourceManager * ChannelManager requires a reference to the ResourceManager so that * channel processing chains can access system wide resources */ + mThreadPoolManager = new ThreadPoolManager(); + mAudioManager = new AudioManager( mThreadPoolManager ); mChannelManager = new ChannelManager( this ); mSourceManager = new SourceManager( this ); mRecordingSourceManager = new RecordingSourceManager( this ); mTunerManager = new TunerManager( this ); mController = new ConfigurationControllerModel( this ); mMapService = new MapService( this ); - mThreadPoolManager = new ThreadPoolManager(); + } + + public AudioManager getAudioManager() + { + return mAudioManager; + } + + public void setAudioManager( AudioManager audioManager ) + { + mAudioManager = audioManager; } public ConfigurationControllerModel getController() diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index 5af34435..b7d5bf41 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -19,13 +19,22 @@ package controller; import java.util.HashMap; import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ThreadPoolManager { + private final static Logger mLog = LoggerFactory.getLogger( ThreadPoolManager.class ); + private int THREAD_POOL_SIZE = 2; private int MAX_TASK_COUNT = 24; @@ -46,7 +55,8 @@ public class ThreadPoolManager { if( mExecutor == null ) { - mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE ); + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); } if( mTasks.size() <= MAX_TASK_COUNT ) @@ -71,7 +81,8 @@ public class ThreadPoolManager { if( mExecutor == null ) { - mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE ); + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); } if( mTasks.size() <= MAX_TASK_COUNT ) @@ -109,6 +120,7 @@ public class ThreadPoolManager public enum ThreadType { + AUDIO_PROCESSING, SOURCE_SAMPLE_PROCESSING, DECIMATION, DECODER; diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 106d507e..08af502d 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -17,17 +17,13 @@ ******************************************************************************/ package controller.channel; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JSeparator; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -35,12 +31,23 @@ import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; import message.Message; +import module.Module; +import module.ProcessingChain; +import module.decode.DecoderFactory; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfigFactory; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEventModel; +import module.decode.event.MessageActivityModel; +import module.decode.state.ChannelState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import record.config.RecordConfiguration; import sample.Listener; +import source.Source; +import source.SourceException; import source.SourceType; import source.config.SourceConfigFactory; import source.config.SourceConfigRecording; @@ -48,19 +55,15 @@ import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; -import source.tuner.frequency.AutomaticFrequencyControl; +import alias.AliasList; import controller.ResourceManager; -import controller.activity.ActivitySummaryFrame; import controller.channel.ChannelEvent.Event; import controller.config.Configuration; import controller.site.Site; -import controller.state.ChannelState; -import controller.state.ChannelState.State; import controller.system.System; -import decode.config.AuxDecodeConfiguration; -import decode.config.DecodeConfigFactory; -import decode.config.DecodeConfiguration; +import eventlog.MessageEventLogger; import eventlog.config.EventLogConfiguration; +import filter.FilterSet; @XmlSeeAlso( { Configuration.class } ) @XmlRootElement( name = "channel" ) @@ -68,15 +71,16 @@ public class Channel extends Configuration { private final static Logger mLog = LoggerFactory.getLogger( Channel.class ); + private static final boolean ENABLED = true; + private static final boolean DISABLED = false; + private static final boolean BROADCAST_CHANGE = true; + public enum ChannelType { STANDARD, TRAFFIC }; - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; private CopyOnWriteArrayList mChannelListeners = new CopyOnWriteArrayList(); @@ -84,8 +88,7 @@ public class Channel extends Configuration private CopyOnWriteArrayList> mMessageListeners = new CopyOnWriteArrayList>(); - private HashMap mTrafficChannels = - new HashMap(); + private Map mTrafficChannels = new HashMap<>(); private DecodeConfiguration mDecodeConfiguration = DecodeConfigFactory.getDefaultDecodeConfiguration(); @@ -105,7 +108,15 @@ public class Channel extends Configuration private boolean mEnabled; private boolean mSelected; private ChannelType mChannelType; + + private CallEventModel mCallEventModel; + private MessageActivityModel mMessageActivityModel; + private MessageEventLogger mEventLogger; private ProcessingChain mProcessingChain; + + private ChannelState mChannelState; + private long mTrafficChannelTimeout = + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; /** * Constructs a new standard channel with a default name of "New Channel" @@ -135,34 +146,46 @@ public class Channel extends Configuration mChannelType = channelType; } - public ChannelType getChannelType() - { - return mChannelType; - } - /** * Indicates if the channel has Automatic or Direct Frequency Control */ public boolean hasFrequencyControl() { return mProcessingChain != null && - mProcessingChain.getDecoder().hasFrequencyCorrectionControl(); + mProcessingChain.hasFrequencyCorrectionControl(); } public long getFrequencyCorrection() { - if( mProcessingChain != null ) + if( hasFrequencyControl() ) { - return mProcessingChain.getDecoder().getFrequencyCorrection(); + return mProcessingChain.getFrequencyCorrectionControl().getErrorCorrection(); } return 0; } + /** + * Sets the channel state traffic channel call end timer to the specified + * value. + */ + public void setTrafficChannelTimeout( long milliseconds ) + { + mTrafficChannelTimeout = milliseconds; + + if( mChannelState != null ) + { + mChannelState.setTrafficChannelTimeout( milliseconds ); + } + } + /** * Sets the resource manager responsible for this channel and registers * the channel manager as a listener to this channel. Fires channel add * channel event to the newly added channel manager channel listener. + * + * Note: resource manager instance is applied after object construction + * since we construct the channel objects from the xml playlist */ public void setResourceManager( ResourceManager resourceManager ) { @@ -170,7 +193,7 @@ public class Channel extends Configuration /* Add system-wide channel listeners onto this channel */ addListeners( mResourceManager.getChannelManager().getChannelListeners() ); - + /* If we're enabled, fire enable changed event to get processing started */ if( mEnabled ) { @@ -200,7 +223,7 @@ public class Channel extends Configuration */ public boolean isProcessing() { - return mProcessingChain != null && mProcessingChain.isProcessing(); + return mProcessingChain != null && mProcessingChain.processing(); } /** @@ -216,100 +239,51 @@ public class Channel extends Configuration { mSelected = selected; - if( mProcessingChain != null && - mProcessingChain.isProcessing() && - mProcessingChain.getAudioOutput() != null ) - { - mProcessingChain.getAudioOutput().setAudioPlaybackEnabled( selected ); - } - fireChannelEvent( Event.CHANGE_SELECTED ); } - public JMenu getContextMenu() + @XmlTransient + public CallEventModel getCallEventModel() { - JMenu menu = new JMenu( mSite + "-" + mName ); - - if( mEnabled ) - { - JMenuItem disable = new JMenuItem( "Disable" ); - disable.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setEnabled( DISABLED, BROADCAST_CHANGE ); - getResourceManager().getPlaylistManager().save(); - } - } ); - - menu.add( disable ); - - menu.add( new JSeparator() ); - - JMenuItem actySummaryItem = - new JMenuItem( "Activity Summary" ); - - actySummaryItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - if( mResourceManager != null ) - { - ProcessingChain chain = getProcessingChain(); - - if( chain != null ) - { - ChannelState state = chain.getChannelState(); - - if( state != null ) - { - String summary = state.getActivitySummary(); - - new ActivitySummaryFrame( summary, - mResourceManager.getController().getTree() ); - } - } - } - } - } ); - - menu.add( actySummaryItem ); - } - else - { - JMenuItem enable = new JMenuItem( "Enable" ); - enable.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setEnabled( ENABLED, BROADCAST_CHANGE ); - getResourceManager().getPlaylistManager().save(); } - } ); - - menu.add( enable ); - } - - menu.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - dispose(); - getResourceManager().getPlaylistManager().save(); } - } ); - - menu.add( deleteItem ); - - - return menu; + return mCallEventModel; } + /** + * Returns the MessageEventLogger for this processing chain + */ + @XmlTransient + public MessageEventLogger getEventLogger() + { + return mEventLogger; + } + + /** + * Sets the MessageEventLogger for this processing chain + */ + public void setEventLogger( MessageEventLogger eventLogger ) + { + mEventLogger = eventLogger; + } + + @XmlTransient + public MessageActivityModel getMessageActivityModel() + { + return mMessageActivityModel; + } + + @XmlTransient + public ChannelState getChannelState() + { + return mChannelState; + } + + @XmlTransient + public ChannelType getChannelType() + { + return mChannelType; + } + + /** * Orderly shutdown method when this channel is going to be deleted. */ @@ -393,6 +367,11 @@ public class Channel extends Configuration return mSystem; } + public boolean hasSystem() + { + return mSystem != null; + } + /** * Sets the owning system for this channel and optionally broadcasts a * system change channel event. @@ -415,6 +394,11 @@ public class Channel extends Configuration return mSite; } + public boolean hasSite() + { + return mSite != null; + } + /** * Sets the owning site for this channel and optionally broadcasts a site * change channel event. @@ -468,7 +452,14 @@ public class Channel extends Configuration if( fireChannelEvent ) { - fireChannelEvent( Event.CHANGE_ENABLED ); + if( enabled ) + { + fireChannelEvent( Event.CHANNEL_ENABLED ); + } + else + { + fireChannelEvent( Event.CHANNEL_DISABLED ); + } } } @@ -522,11 +513,6 @@ public class Channel extends Configuration public void setDecodeConfiguration( DecodeConfiguration config ) { mDecodeConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateDecoder(); - } } /** @@ -544,11 +530,6 @@ public class Channel extends Configuration public void setAuxDecodeConfiguration( AuxDecodeConfiguration config ) { mAuxDecodeConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateDecoder(); - } } /** @@ -581,11 +562,6 @@ public class Channel extends Configuration public void setSourceConfiguration( SourceConfiguration config ) { mSourceConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateSource(); - } } /** @@ -618,11 +594,6 @@ public class Channel extends Configuration public void setEventLogConfiguration( EventLogConfiguration config ) { mEventLogConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateEventLogging(); - } } /** @@ -655,11 +626,6 @@ public class Channel extends Configuration public void setRecordConfiguration( RecordConfiguration config ) { mRecordConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateRecording(); - } } /** @@ -730,38 +696,124 @@ public class Channel extends Configuration */ private void start() { - if( mEnabled && mProcessingChain == null && mResourceManager != null ) + if( mEnabled && mResourceManager != null ) { - mProcessingChain = new ProcessingChain( this, mResourceManager ); + setupChannelState(); + + try + { + setup(); + + mProcessingChain.start(); + + fireChannelEvent( Event.CHANNEL_PROCESSING_STARTED ); + } + catch( SourceException se ) + { + mLog.error( "Error obtaining source for channel" ); + } + } + } + + private void setupChannelState() + { + if( mChannelState == null ) + { + mChannelState = new ChannelState( mResourceManager + .getThreadPoolManager(), mChannelType ); + + mChannelState.setTrafficChannelTimeout( mTrafficChannelTimeout ); + } + } + + /** + * Sets up the channel and prepares to commense processing. Sets up the + * processing chain and requests a source. + * + * @throws SourceException - if no source is available or if there is an + * error obtaining a source + */ + public void setup() throws SourceException + { + setupProcessingChain(); + + setupSource(); + + //TODO: Add logging + //TODO: Add recorder(s) + } + + private void setupProcessingChain() + { + if( mProcessingChain == null ) + { + mProcessingChain = new ProcessingChain( + mResourceManager.getThreadPoolManager() ); + + /* Get the optional alias list for the decode modules to use */ + AliasList aliasList = mResourceManager.getPlaylistManager() + .getPlayist().getAliasDirectory().getAliasList( mAliasListName ); + + /* Add the channel state as a module */ + mProcessingChain.addModule( mChannelState ); + + /* Processing Modules */ + List modules = DecoderFactory.getModules( mChannelType, + mResourceManager, mDecodeConfiguration, mAuxDecodeConfiguration, + aliasList ); + mProcessingChain.addModules( modules ); + + /* Get message filters for the set of processing modules */ + FilterSet messageFilter = DecoderFactory.getMessageFilters( modules ); + + /* Setup the message activity model and add message listeners */ + mMessageActivityModel = new MessageActivityModel( messageFilter ); + mProcessingChain.addMessageListener( mMessageActivityModel ); + + /* Add audio manager as listener for audio packets */ + mProcessingChain.addAudioPacketListener( + mResourceManager.getAudioManager() ); /* Add system-wide message listeners */ - mProcessingChain.addListeners( + mProcessingChain.addMessageListeners( mResourceManager.getChannelManager().getMessageListeners() ); - - /* Add individual message listeners */ - mProcessingChain.addListeners( mMessageListeners ); - - mProcessingChain.start(); - fireChannelEvent( Event.PROCESSING_STARTED ); - } + /* Add individual message listeners */ + mProcessingChain.addMessageListeners( mMessageListeners ); - /* If this is a traffic channel, override the call fade timeout before - * we set the state to call, so that it will auto-expire after the - * fade timeout and be auto deleted */ - if( mChannelType == ChannelType.TRAFFIC ) + /* Create the call event model to receive call events */ + mCallEventModel = new CallEventModel(); + mProcessingChain.addCallEventListener( mCallEventModel ); + } + } + + /** + * Attempts to obtain a source and apply it to the processing chain. + * + * @throws SourceException - if the processing chain is not setup or if + * there is an error produced while obtaining the source + */ + private void setupSource() throws SourceException + { + if( mProcessingChain != null ) { - if( mProcessingChain.isProcessing() ) + Source source = mResourceManager.getSourceManager() + .getSource( getSourceConfiguration(), getDecodeConfiguration() + .getDecoderType().getChannelBandwidth() ); + + if( source != null ) { - mProcessingChain.getChannelState().setCallFadeTimeout( 40000 ); - mProcessingChain.getChannelState().setState( State.CALL ); + mProcessingChain.setSource( source ); } else { - mProcessingChain.getChannelState().setCallFadeTimeout( 5000 ); - /* Channel state should already be no_tuner */ + throw new SourceException( "Couldn't obtain tuner channel source" ); } - + } + else + { + throw new SourceException( "Cannot obtain source for a null " + + "processing chain - setup the processing chain first" ); } } @@ -770,27 +822,14 @@ public class Channel extends Configuration */ private void stop() { - /* Stop and remove any traffic channels */ - for( Channel traffic: mTrafficChannels.values() ) - { - traffic.stop(); - traffic.dispose(); - } - - mTrafficChannels.clear(); - if( !mEnabled && mProcessingChain != null ) { mProcessingChain.stop(); - mProcessingChain.dispose(); - - mProcessingChain = null; - mSelected = false; } - fireChannelEvent( Event.PROCESSING_STOPPED ); + fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED ); } /** @@ -798,9 +837,18 @@ public class Channel extends Configuration */ public void fireChannelEvent( Event event ) { + ChannelEvent channelEvent = new ChannelEvent( this, event ); + for( ChannelEventListener listener: mChannelListeners ) { - listener.channelChanged( new ChannelEvent( this, event ) ); + listener.channelChanged( channelEvent ); + } + + /* Send event to processing chain so modules can respond to channel + * selection events */ + if( mProcessingChain != null ) + { + mProcessingChain.getChannelEventListener().receive( channelEvent ); } } @@ -813,7 +861,7 @@ public class Channel extends Configuration if( !mChannelListeners.contains( listener ) ) { mChannelListeners.add( listener ); - + listener.channelChanged( new ChannelEvent( this, Event.CHANNEL_ADDED ) ); } @@ -850,7 +898,7 @@ public class Channel extends Configuration if( isProcessing() ) { - mProcessingChain.addListener( listener ); + mProcessingChain.addMessageListener( listener ); } } @@ -858,9 +906,9 @@ public class Channel extends Configuration { mMessageListeners.remove( listener ); - if( isProcessing() ) + if( mProcessingChain != null ) { - mProcessingChain.removeListener( listener ); + mProcessingChain.removeMessageListener( listener ); } } @@ -870,13 +918,15 @@ public class Channel extends Configuration */ public boolean isWithin( long minimum, long maximum ) { - return getTunerChannel() != null && - getTunerChannel().isWithin( minimum, maximum ); + TunerChannel tunerChannel = getTunerChannel(); + + return tunerChannel != null && + tunerChannel.isWithin( minimum, maximum ); } - public void addTrafficChannel( int channelNumber, Channel channel ) + public void addTrafficChannel( String channelID, Channel channel ) { - mTrafficChannels.put( channelNumber, channel ); + mTrafficChannels.put( channelID, channel ); } public boolean hasTrafficChannel( Channel channel ) @@ -886,22 +936,11 @@ public class Channel extends Configuration public void removeTrafficChannel( Channel channel ) { - int keyToRemove = -1; + String name = channel.getChannelDisplayName(); - if( mTrafficChannels.values().contains( channel ) ) + if( mTrafficChannels.containsKey( name ) ) { - for( Integer channelNumber: mTrafficChannels.keySet() ) - { - if( mTrafficChannels.get( channelNumber ).equals( channel ) ) - { - keyToRemove = channelNumber; - } - } - } - - if( keyToRemove != -1 ) - { - mTrafficChannels.remove( keyToRemove ); + mTrafficChannels.remove( name ); } } @@ -910,8 +949,9 @@ public class Channel extends Configuration return Collections.unmodifiableCollection( mTrafficChannels.values() ); } - public boolean hasTrafficChannel( int channelNumber ) + public boolean hasTrafficChannel( String name ) { - return mTrafficChannels.containsKey( channelNumber ); + return mTrafficChannels.containsKey( name ); } + } diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 082829a6..7f3d5bd0 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -35,6 +35,8 @@ import javax.swing.JTextField; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; +import module.decode.AuxDecodeComponentEditor; +import module.decode.DecodeComponentEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -47,8 +49,6 @@ import alias.AliasList; import com.jidesoft.swing.JideTabbedPane; import controller.Editor; -import decode.AuxDecodeComponentEditor; -import decode.DecodeComponentEditor; import eventlog.EventLogComponentEditor; public class ChannelEditor extends Editor implements ActionListener @@ -99,7 +99,7 @@ public class ChannelEditor extends Editor implements ActionListener /** * ComboBox: Alias Lists */ - add( new JLabel( "Aliases:" ) ); + add( new JLabel( "Alias List:" ) ); mComboAliasLists = new JComboBox(); diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java index df2fc15b..a8b88eab 100644 --- a/src/controller/channel/ChannelEvent.java +++ b/src/controller/channel/ChannelEvent.java @@ -34,19 +34,26 @@ public class ChannelEvent { CHANGE_ALIAS_LIST, CHANGE_DECODER, - CHANGE_ENABLED, CHANGE_EVENT_LOGGER, CHANGE_NAME, + CHANGE_SELECTED, CHANGE_SITE, CHANGE_RECORDER, - CHANGE_SELECTED, CHANGE_SOURCE, CHANGE_SYSTEM, - CHANNEL_ADDED, + CHANNEL_ADDED, CHANNEL_DELETED, + + CHANNEL_ENABLED, + CHANNEL_DISABLED, - PROCESSING_STARTED, - PROCESSING_STOPPED, + CHANNEL_PROCESSING_STARTED, + CHANNEL_PROCESSING_STOPPED, + + CHANNEL_STATE_RESET, + + TRAFFIC_CHANNEL_ADDED, + TRAFFIC_CHANNEL_DELETED; } } diff --git a/src/controller/channel/ChannelManager.java b/src/controller/channel/ChannelManager.java index ca475413..221b1253 100644 --- a/src/controller/channel/ChannelManager.java +++ b/src/controller/channel/ChannelManager.java @@ -17,8 +17,13 @@ ******************************************************************************/ package controller.channel; +import gui.SDRTrunk; + import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import alias.action.AliasActionManager; import message.Message; import sample.Listener; @@ -26,6 +31,8 @@ import controller.ResourceManager; public class ChannelManager implements ChannelEventListener { + private final static Logger mLog = LoggerFactory.getLogger( ChannelManager.class ); + private ArrayList mChannels = new ArrayList(); private ArrayList mChannelListeners = @@ -56,13 +63,13 @@ public class ChannelManager implements ChannelEventListener { switch( event.getEvent() ) { - case CHANNEL_ADDED: + case CHANNEL_ENABLED: if( !mChannels.contains( event.getChannel() ) ) { mChannels.add( event.getChannel() ); } break; - case CHANNEL_DELETED: + case CHANNEL_DISABLED: mChannels.remove( event.getChannel() ); break; } diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java index de687f37..4f19f59a 100644 --- a/src/controller/channel/ChannelNode.java +++ b/src/controller/channel/ChannelNode.java @@ -78,17 +78,17 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene { /* Refresh the node for each of these events */ case CHANGE_DECODER: - case CHANGE_ENABLED: case CHANGE_NAME: case CHANGE_SELECTED: case CHANGE_SITE: case CHANGE_SYSTEM: - case PROCESSING_STARTED: - case PROCESSING_STOPPED: + case CHANNEL_ENABLED: + case CHANNEL_PROCESSING_STARTED: + case CHANNEL_PROCESSING_STOPPED: this.refresh(); break; /* We're being deleted, so cleanup */ - case CHANNEL_DELETED: + case CHANNEL_DISABLED: delete(); break; default: @@ -154,7 +154,7 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene public JPopupMenu getContextMenu() { JPopupMenu popupMenu = new JPopupMenu( "Channel Menu" ); - popupMenu.add( getChannel().getContextMenu() ); +// popupMenu.add( getChannel().getContextMenu() ); return popupMenu; } diff --git a/src/controller/channel/ChannelSelectionListener.java b/src/controller/channel/ChannelSelectionListener.java index 9719ea6f..5bd1a8c9 100644 --- a/src/controller/channel/ChannelSelectionListener.java +++ b/src/controller/channel/ChannelSelectionListener.java @@ -17,7 +17,9 @@ ******************************************************************************/ package controller.channel; +import module.ProcessingChainOld; + public interface ChannelSelectionListener { - public void channelSelected( ProcessingChain channel ); + public void channelSelected( ProcessingChainOld channel ); } diff --git a/src/controller/channel/IChannelEventListener.java b/src/controller/channel/IChannelEventListener.java new file mode 100644 index 00000000..3ec41b53 --- /dev/null +++ b/src/controller/channel/IChannelEventListener.java @@ -0,0 +1,8 @@ +package controller.channel; + +import sample.Listener; + +public interface IChannelEventListener +{ + public Listener getChannelEventListener(); +} diff --git a/src/controller/channel/IChannelEventProvider.java b/src/controller/channel/IChannelEventProvider.java new file mode 100644 index 00000000..330e22ab --- /dev/null +++ b/src/controller/channel/IChannelEventProvider.java @@ -0,0 +1,9 @@ +package controller.channel; + +import sample.Listener; + +public interface IChannelEventProvider +{ + public void setChannelEventListener( Listener listener ); + public void removeChannelEventListener(); +} diff --git a/src/controller/channel/ChannelMap.java b/src/controller/channel/map/ChannelMap.java similarity index 98% rename from src/controller/channel/ChannelMap.java rename to src/controller/channel/map/ChannelMap.java index 60079530..3ec928a0 100644 --- a/src/controller/channel/ChannelMap.java +++ b/src/controller/channel/map/ChannelMap.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.util.ArrayList; diff --git a/src/controller/channel/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java similarity index 97% rename from src/controller/channel/ChannelMapEditor.java rename to src/controller/channel/map/ChannelMapEditor.java index 727b79c0..11ede472 100644 --- a/src/controller/channel/ChannelMapEditor.java +++ b/src/controller/channel/map/ChannelMapEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.awt.Component; import java.awt.Dimension; @@ -80,7 +80,6 @@ public class ChannelMapEditor extends JPanel implements ActionListener @Override public void focusLost( FocusEvent arg0 ) { - mLog.info( "text field action event fired, new value:" + mNameText.getText() ); mChannelMapCopy.setName( mNameText.getText() ); } } ); @@ -208,7 +207,6 @@ public class ChannelMapEditor extends JPanel implements ActionListener @Override public void actionPerformed( ActionEvent e ) { - mLog.info( "Action Performed:" + e.getActionCommand() ); } @Override diff --git a/src/controller/channel/ChannelMapList.java b/src/controller/channel/map/ChannelMapList.java similarity index 98% rename from src/controller/channel/ChannelMapList.java rename to src/controller/channel/map/ChannelMapList.java index 38ba11ee..7a43b25b 100644 --- a/src/controller/channel/ChannelMapList.java +++ b/src/controller/channel/map/ChannelMapList.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.util.ArrayList; diff --git a/src/controller/channel/ChannelMapListNode.java b/src/controller/channel/map/ChannelMapListNode.java similarity index 98% rename from src/controller/channel/ChannelMapListNode.java rename to src/controller/channel/map/ChannelMapListNode.java index 158b21d4..208e22a4 100644 --- a/src/controller/channel/ChannelMapListNode.java +++ b/src/controller/channel/map/ChannelMapListNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; diff --git a/src/controller/channel/ChannelMapModel.java b/src/controller/channel/map/ChannelMapModel.java similarity index 99% rename from src/controller/channel/ChannelMapModel.java rename to src/controller/channel/map/ChannelMapModel.java index 32237929..50da7dfe 100644 --- a/src/controller/channel/ChannelMapModel.java +++ b/src/controller/channel/map/ChannelMapModel.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import javax.swing.table.AbstractTableModel; diff --git a/src/controller/channel/ChannelMapNode.java b/src/controller/channel/map/ChannelMapNode.java similarity index 98% rename from src/controller/channel/ChannelMapNode.java rename to src/controller/channel/map/ChannelMapNode.java index d9b68419..7a0712e0 100644 --- a/src/controller/channel/ChannelMapNode.java +++ b/src/controller/channel/map/ChannelMapNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.awt.event.ActionEvent; diff --git a/src/controller/channel/ChannelRange.java b/src/controller/channel/map/ChannelRange.java similarity index 98% rename from src/controller/channel/ChannelRange.java rename to src/controller/channel/map/ChannelRange.java index edc861b1..a9814239 100644 --- a/src/controller/channel/ChannelRange.java +++ b/src/controller/channel/map/ChannelRange.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; diff --git a/src/controller/config/Configuration.java b/src/controller/config/Configuration.java index ff979c4a..8a46d301 100644 --- a/src/controller/config/Configuration.java +++ b/src/controller/config/Configuration.java @@ -20,10 +20,10 @@ package controller.config; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfiguration; import record.config.RecordConfiguration; import source.config.SourceConfiguration; -import decode.config.AuxDecodeConfiguration; -import decode.config.DecodeConfiguration; import eventlog.config.EventLogConfiguration; @XmlSeeAlso( { AuxDecodeConfiguration.class, diff --git a/src/controller/state/AuxChannelState.java b/src/controller/state/AuxChannelState.java deleted file mode 100644 index 218c37cc..00000000 --- a/src/controller/state/AuxChannelState.java +++ /dev/null @@ -1,80 +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 controller.state; - -import message.Message; -import sample.Broadcaster; -import sample.Listener; -import controller.state.ChannelState.ChangedAttribute; - -/** - * ChannelState for auxiliary decoders. - */ -public abstract class AuxChannelState implements Listener -{ - protected ChannelState mParentChannelState; - protected Broadcaster mChangeBroadcaster = - new Broadcaster(); - - public AuxChannelState( ChannelState parentState ) - { - mParentChannelState = parentState; - } - - /** - * Prepare for garbage collect - */ - public void dispose() - { - mChangeBroadcaster.dispose(); - mParentChannelState = null; - } - - /** - * Call fade - change display and await a call to reset() - */ - public abstract void fade(); - - /** - * Activity summary for this decoder - */ - public abstract String getActivitySummary(); - - /** - * Call end - cleanup the display - */ - public abstract void reset(); - - public void addListener( Listener listener ) - { - mChangeBroadcaster.addListener( listener ); - } - - public void removeListener( Listener listener ) - { - mChangeBroadcaster.removeListener( listener ); - } - - public void broadcastChange( ChangedAttribute attribute ) - { - if( mChangeBroadcaster != null ) - { - mChangeBroadcaster.broadcast( attribute ); - } - } -} diff --git a/src/controller/state/ChannelState.java b/src/controller/state/ChannelState.java deleted file mode 100644 index 3f395086..00000000 --- a/src/controller/state/ChannelState.java +++ /dev/null @@ -1,697 +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 controller.state; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.swing.JTable; - -import message.Message; -import message.MessageFilterFactory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import properties.SystemProperties; -import sample.Broadcaster; -import sample.Listener; -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import audio.inverted.AudioType; -import audio.inverted.IAudioTypeListener; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.activity.CallEventAliasCellRenderer; -import controller.activity.CallEventModel; -import controller.activity.MessageActivityModel; -import controller.channel.Channel.ChannelType; -import controller.channel.ProcessingChain; -import filter.FilterSet; - -/** - * ChannelState provides a state machine for tracking a voice or data call - * through it's life cycle of CALL/DATA, FADE, and then END, and then back to - * an IDLE state. This class also provides squelch control for unsquelching - * sound to the speakers, and/or sound to an audio recorder - * - * State Descriptions: - * IDLE No voice or data call activity - * CALL/DATA A voice or data call has started, or is continuing - * CONTROL Control channel - * 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 - * END The phase after the voice or data call FADE, when the call is - * now over, and the gui should be reset to IDLE. - * - * When a call is started, invoke mTimerService.startFadeTimer() to start the - * call fade monitoring timer process that will automatically signal a call end - * once no more call signalling has been detected after the fade timeout has - * occurred, by invoking the fade() method. - * - * When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke - * the mTimerService.startResetTimer(). Once the reset timeout occurs, - * the reset() method will be called, allowing the channel state to perform - * any reset actions, like clearing status and id labels on any view panels. - * - * AuxChannelState class allows for auxiliary decoders like Fleetsync to both - * have a state and leverage the state machine of the parent ChannelState. - * This allows a non-squelching channel state like for conventional fm to have - * an aux decoder like fleetsync, and allow the fleetsync auxchannelstate to - * leverage the call/end states of the parent conventional channelstate. - * - */ -public abstract class ChannelState implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( ChannelState.class ); - - private State mState = State.IDLE; - private Object mCallFadeLock = new Object(); - private Object mCallResetLock = new Object(); - private long mFadeTimeout = -1; - private long mResetTimeout = -1; - - protected long mCallFadeTimeout; - protected long mCallResetTimeout; - protected TimerService mTimerService = new TimerService(); - protected ArrayList mSquelchListeners = - new ArrayList(); - protected ArrayList mAuxChannelStates = - new ArrayList(); - protected Broadcaster mChangeBroadcaster = - new Broadcaster(); - protected IAudioTypeListener mAudioTypeListener; - protected AudioType mAudioType = AudioType.NORMAL; - - protected CallEventModel mCallEventModel = new CallEventModel(); - - protected JTable mCallEventTable; - - protected MessageActivityModel mMessageActivityModel; - protected JTable mMessageActivityTable; - - protected ProcessingChain mProcessingChain; - - protected AliasList mAliasList; - - protected CallEvent mCurrentCallEvent; - - public ChannelState( ProcessingChain processingChain, AliasList aliasList ) - { - mProcessingChain = processingChain; - mAliasList = aliasList; - - - /* Register to receive decoded messages from the processing chain */ - mProcessingChain.addListener( this ); - - SystemProperties props = SystemProperties.getInstance(); - mCallFadeTimeout = props.get( "call.fade.timeout", 2000 ); - mCallResetTimeout = props.get( "call.reset.timeout", 4000 ); - - /* Get a message filter for the primary decoder and aux decoders */ - FilterSet messageFilter = MessageFilterFactory - .getMessageFilter( mProcessingChain.getChannel() ); - - mMessageActivityModel = new MessageActivityModel( messageFilter ); - } - - public void setCurrentCallEvent( CallEvent callEvent ) - { - mCurrentCallEvent = callEvent; - } - - public CallEvent getCurrentCallEvent() - { - return mCurrentCallEvent; - } - - /** - * Allows setting the call event model to use for traffic channels, so that - * all traffic channels can share the parent control channel call event - * model. - * @param model - */ - public void setCallEventModel( CallEventModel model ) - { - mCallEventModel = model; - } - - public CallEventModel getCallEventModel() - { - return mCallEventModel; - } - - public void receiveCallEvent( CallEvent event ) - { - mCallEventModel.add( event ); - } - - public void dispose() - { - mTimerService.dispose(); - - mAuxChannelStates.clear(); - mSquelchListeners.clear(); - mChangeBroadcaster.dispose(); - mChangeBroadcaster = null; - - if( mProcessingChain.getChannel().getChannelType() == ChannelType.STANDARD ) - { - mCallEventModel.dispose(); - } - /* If we're a traffic channel, set the call event model to null so that - * we don't dispose the parent control channel's call event model */ - else - { - mCallEventModel = null; - } - - mMessageActivityModel.dispose(); - mMessageActivityModel = null; - - mAliasList = null; - mAudioTypeListener = null; - mCallEventModel = null; - mProcessingChain = null; - } - - /** - * Channel Activity Summary - implemented by the sub-class. - */ - public abstract String getActivitySummary(); - - public AliasList getAliasList() - { - return mAliasList; - } - - public boolean hasAliasList() - { - return mAliasList != null; - } - - public void broadcastChange( final ChangedAttribute attribute ) - { - mChangeBroadcaster.broadcast( attribute ); - } - - public void setCallFadeTimeout( long milliseconds ) - { - mCallFadeTimeout = milliseconds; - } - - public JTable getCallEventTable() - { - if( mCallEventTable == null ) - { - mCallEventTable = new JTable( mCallEventModel ); - mCallEventTable.setAutoCreateRowSorter( true ); - mCallEventTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); - - CallEventAliasCellRenderer renderer = - new CallEventAliasCellRenderer( mProcessingChain - .getResourceManager().getSettingsManager() ); - - mCallEventTable.getColumnModel().getColumn( 3 ) - .setCellRenderer( renderer ); - - mCallEventTable.getColumnModel().getColumn( 5 ) - .setCellRenderer( renderer ); - } - - return mCallEventTable; - } - - /** - * Returns the message activity model. - */ - public MessageActivityModel getMessageActivityModel() - { - return mMessageActivityModel; - } - - public void addAuxChannelState( AuxChannelState state ) - { - mAuxChannelStates.add( state ); - } - - public ArrayList getAuxChannelStates() - { - return mAuxChannelStates; - } - - public void receive( Message message ) - { - if( mMessageActivityModel != null ) - { - mMessageActivityModel.receive( message ); - } - - for( AuxChannelState state: mAuxChannelStates ) - { - if( state != null ) - { - state.receive( message ); - } - } - } - - public ProcessingChain getProcessingChain() - { - return mProcessingChain; - } - - public State getState() - { - return mState; - } - - public void setState( State state ) - { - /* Let all CALL state changes go through in order to update the fade - * timer, otherwise, don't update the state if it hasn't changed */ - if( mState != state || state == State.CALL || state == State.DATA || state == State.ENCRYPTED ) - { - if( mState.canChangeTo( state ) ) - { - mState = state; - - switch( state ) - { - case CONTROL: - setSquelchState( SquelchState.SQUELCH ); - break; - case CALL: - setSquelchState( SquelchState.UNSQUELCH ); - mTimerService.stopResetTimer(); - mTimerService.startFadeTimer(); - break; - case DATA: - mTimerService.stopResetTimer(); - mTimerService.startFadeTimer(); - break; - case ENCRYPTED: - setSquelchState( SquelchState.SQUELCH ); - mTimerService.stopResetTimer(); - mTimerService.startFadeTimer(); - case NO_TUNER: - mTimerService.stopResetTimer(); - if( getProcessingChain().getChannel() - .getChannelType() == ChannelType.TRAFFIC ) - { - mTimerService.startFadeTimer(); - } - break; - case FADE: - mTimerService.stopFadeTimer(); - mTimerService.startResetTimer(); - setSquelchState( SquelchState.SQUELCH ); - if( mAudioTypeListener != null ) - { - mAudioTypeListener.setAudioType( mAudioType ); - } - break; - case END: - mTimerService.stopResetTimer(); - mTimerService.stopFadeTimer(); - setSquelchState( SquelchState.SQUELCH ); - if( mAudioTypeListener != null ) - { - mAudioTypeListener.setAudioType( mAudioType ); - } - - /* If this is a traffic channel, stop it */ - if( mProcessingChain.getChannel() - .getChannelType() == ChannelType.TRAFFIC ) - { - mProcessingChain.getChannel().setEnabled( false ); - } - break; - case IDLE: - break; - default: - break; - } - - broadcastChange( ChangedAttribute.CHANNEL_STATE ); - } - else - { - mLog.error( "Channel State - can't change from [" + - mState.toString() + "] to [" + state.toString() + "]" ); - } - } - } - - /** - * End method to reset the channel state once a call has completely - * ended and now you want to return the channel state to idle - */ - protected void reset() - { - setState( State.END ); - - for( AuxChannelState state: mAuxChannelStates ) - { - state.reset(); - } - - //Reset the state to idle - setState( State.IDLE ); - } - - /** - * Method to perform any call fade actions. On fade, you should change the - * channel state to fading, and construct a call-reset timer to execute a few - * seconds later. This allows the view to signal to the user that the call - * has faded/ended, and display faded information to the user, until the - * cleanup() task is run. - */ - protected void fade( CallEventType type ) - { - for( AuxChannelState state: mAuxChannelStates ) - { - state.fade(); - } - - setState( State.FADE ); - } - - public void setListener( IAudioTypeListener listener ) - { - mAudioTypeListener = listener; - } - - public void addListener( SquelchListener listener ) - { - mSquelchListeners.add( listener ); - } - - public void removeListener( SquelchListener listener ) - { - mSquelchListeners.remove( listener ); - } - - protected void setSquelchState( SquelchState state ) - { - Iterator it = mSquelchListeners.iterator(); - - while( it.hasNext() ) - { - it.next().setSquelch( state ); - } - } - - /** - * Timer service - */ - public class TimerService extends ScheduledThreadPoolExecutor - { - private long mTimerMonitorRevisitRate = 100; //100 milliseconds - - private ScheduledFuture mTimerMonitor; - - public TimerService() - { - super( 1 ); - } - - public void dispose() - { - if( mTimerMonitor != null ) - { - mTimerMonitor.cancel( true ); - } - - this.shutdownNow(); - } - - private void checkMonitor() - { - boolean createTimer = false; - - synchronized( mCallFadeLock ) - { - if( mFadeTimeout != -1 ) - { - createTimer = true; - } - } - - synchronized( mCallResetLock ) - { - if( mResetTimeout != -1 ) - { - createTimer = true; - } - } - - if( createTimer ) - { - //Check to see if the timer is already running, if not, start it - - if( mTimerMonitor == null || mTimerMonitor.isDone() ) - { - scheduleAtFixedRate( new StateTimeoutMonitor(), - mTimerMonitorRevisitRate, - mTimerMonitorRevisitRate, - TimeUnit.MILLISECONDS ); - } - } - else - { - if( mTimerMonitor != null ) - { - mTimerMonitor.cancel( true ); - } - } - } - - public void startFadeTimer() - { - synchronized( mCallFadeLock ) - { - mFadeTimeout = System.currentTimeMillis() + mCallFadeTimeout; - } - - checkMonitor(); - } - - public void resetFadeTimer() - { - startFadeTimer(); - } - - public void stopFadeTimer() - { - synchronized( mCallFadeLock ) - { - mFadeTimeout = -1; - } - - checkMonitor(); - } - - public void startResetTimer() - { - synchronized( mCallResetLock ) - { - mResetTimeout = System.currentTimeMillis() + mCallResetTimeout; - } - - checkMonitor(); - } - - public void stopResetTimer() - { - synchronized( mCallResetLock ) - { - mResetTimeout = -1; - } - - checkMonitor(); - } - } - - public class StateTimeoutMonitor implements Runnable - { - @Override - public void run() - { - long now = System.currentTimeMillis(); - - synchronized( mCallFadeLock ) - { - if( mFadeTimeout != -1 && now >= mFadeTimeout ) - { - mFadeTimeout = -1; - - fade( CallEventType.CALL_TIMEOUT ); - } - } - - synchronized( mCallResetLock ) - { - if( mResetTimeout != -1 && now >= mResetTimeout ) - { - mResetTimeout = -1; - - reset(); - } - } - } - } - - - public enum State - { - IDLE( "IDLE" ) { - @Override - public boolean canChangeTo( State state ) - { - return true; - } - }, - CALL( "CALL" ) { - @Override - public boolean canChangeTo( State state ) - { - return state == CALL || - state == CONTROL || - state == DATA || - state == ENCRYPTED || - state == FADE; - } - }, - DATA( "DATA" ) { - @Override - public boolean canChangeTo( State state ) - { - return state == CALL || - state == CONTROL || - state == DATA || - state == State.ENCRYPTED || - state == FADE; - } - }, - ENCRYPTED( "ENCRYPTED" ) - { - @Override - public boolean canChangeTo( State state ) - { - return state == FADE; - } - }, - CONTROL( "CONTROL" ) - { - @Override - public boolean canChangeTo( State state ) - { - return true; - } - }, - FADE( "FADE" ) { - @Override - public boolean canChangeTo( State state ) - { - return state != FADE; //All states except fade allowed - } - }, - END( "END" ) { - @Override - public boolean canChangeTo( State state ) - { - return state != FADE; //Fade is only disallowed state - } - }, - NO_TUNER( "NO TUNER" ) { - @Override - public boolean canChangeTo( State state ) - { - return state == FADE || state == END; - } - }; - - private String mDisplayValue; - - private State( String displayValue ) - { - mDisplayValue = displayValue; - } - - public abstract boolean canChangeTo( State state ); - - public String getDisplayValue() - { - return mDisplayValue; - } - } - - /** - * Registers a listener to receive channel state attribute changes - */ - public void addListener( Listener listener ) - { - mChangeBroadcaster.addListener( listener ); - } - - /** - * Removes a listener from receiving channel state attribute changes - */ - public void removeListener( Listener listener ) - { - mChangeBroadcaster.removeListener( listener ); - } - - public enum ChangedAttribute - { - AUDIO_TYPE, - CHANNEL_NUMBER, - CHANNEL_NAME, - CHANNEL_SITE_NUMBER, - CHANNEL_SITE_NUMBER_ALIAS, - CHANNEL_STATE, - DESCRIPTION, - FROM_TALKGROUP, - FROM_TALKGROUP_ALIAS, - FROM_TALKGROUP_TYPE, - MESSAGE, - MESSAGE_TYPE, - NAC, - SITE, - SITE_ALIAS, - SITE_NAME, - SOURCE, - SYSTEM, - SYSTEM_NAME, - SQUELCH, - TO_TALKGROUP, - TO_TALKGROUP_ALIAS, - TO_TALKGROUP_TYPE, - WACN; - } -} diff --git a/src/controller/state/ChannelStatePanel.java b/src/controller/state/ChannelStatePanel.java deleted file mode 100644 index 4c8e6b3f..00000000 --- a/src/controller/state/ChannelStatePanel.java +++ /dev/null @@ -1,388 +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 controller.state; - -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Font; -import java.awt.GradientPaint; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.util.HashMap; - -import javax.swing.JMenu; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; -import sample.Listener; -import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; -import settings.Setting; -import settings.SettingChangeListener; -import settings.SettingsManager; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; - -public abstract class ChannelStatePanel extends JPanel - implements Listener, - SettingChangeListener -{ - private static final long serialVersionUID = 1L; - - protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - protected Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - - protected Color mColorChannelBackground; - protected Color mColorChannelSelected; - protected Color mColorTopCall; - protected Color mColorMiddleCall; - protected Color mColorTopControl; - protected Color mColorMiddleControl; - protected Color mColorTopData; - protected Color mColorMiddleData; - protected Color mColorTopFade; - protected Color mColorMiddleFade; - protected Color mColorTopIdle; - protected Color mColorMiddleIdle; - protected Color mColorTopNoTuner; - protected Color mColorMiddleNoTuner; - protected Color mColorLabelDetails; - protected Color mColorLabelDecoder; - protected Color mColorLabelAuxDecoder; - - private HashMap mTrafficPanels = - new HashMap(); - - protected Channel mChannel; - - public ChannelStatePanel( Channel channel ) - { - mChannel = channel; - - /* Register to receive settings updates */ - getSettingsManager().addListener( this ); - - /* Register to receive channel updates */ - mChannel.getProcessingChain().getChannelState().addListener( this ); - - getColors(); - - setLayout( new MigLayout( "insets 3 2 2 2", "[grow,fill]", "[]0[]0[]") ); - } - - public SettingsManager getSettingsManager() - { - return mChannel.getResourceManager().getSettingsManager(); - } - - public void dispose() - { - mTrafficPanels.clear(); - mChannel = null; - } - - private void getColors() - { - mColorChannelBackground = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_BACKGROUND ).getColor(); - mColorChannelSelected = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ).getColor(); - mColorTopCall = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ).getColor(); - mColorMiddleCall = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ).getColor(); - mColorTopControl = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ).getColor(); - mColorMiddleControl = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ).getColor(); - mColorTopData = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ).getColor(); - mColorMiddleData = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ).getColor(); - mColorTopFade = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ).getColor(); - mColorMiddleFade = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ).getColor(); - mColorTopIdle = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ).getColor(); - mColorMiddleIdle = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ).getColor(); - mColorTopNoTuner = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ).getColor(); - mColorMiddleNoTuner = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ).getColor(); - - mColorLabelDetails = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); - mColorLabelDecoder = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); - mColorLabelAuxDecoder = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor(); - } - - public HashMap getTrafficPanels() - { - return mTrafficPanels; - } - - public JMenu getContextMenu() - { - return mChannel.getContextMenu(); - } - - public Channel getChannel() - { - return mChannel; - } - - public ChannelState getChannelState() - { - if( mChannel.getProcessingChain() != null ) - { - return mChannel.getProcessingChain().getChannelState(); - } - - return null; - } - - public boolean isSelected() - { - return mChannel.isSelected(); - } - - public void addPanel( final JPanel panel ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - add( panel, "span,wrap" ); - validate(); - } - } ); - } - - public void removePanel( final JPanel panel ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - remove( panel ); - - validate(); - - if( getParent() != null ) - { - getParent().validate(); - } - - if( panel instanceof ChannelStatePanel ) - { - ((ChannelStatePanel)panel).dispose(); - } - else if( panel instanceof AuxStatePanel ) - { - ((AuxStatePanel)panel).dispose(); - } - } - } ); - } - - public void trafficChannelAdded( Channel traffic ) - { - //Disregard -- sub class can override and implement - } - - public void trafficChannelDeleted( Channel traffic ) - { - ChannelStatePanel panelToRemove = - getTrafficPanels().remove( traffic ); - - removePanel( panelToRemove ); - } - - public void setSelected( boolean selected ) - { - for( ChannelStatePanel traffic: getTrafficPanels().values() ) - { - traffic.setSelected( false ); - } - - mChannel.setSelected( selected ); - - repaint(); - } - - private GradientPaint getGradient( Color top, Color middle ) - { - return new GradientPaint( 0f, -50.0f, top, - 0f, (float)getHeight() / 2.2f, middle, true ); - } - - @Override - protected void paintComponent(Graphics g) - { - super.paintComponent( g ); - - setBackground( mColorChannelBackground ); - - Graphics2D g2 = (Graphics2D)g.create(); - - Paint p = null; - - ChannelState channelState = getChannelState(); - - if( channelState != null ) - { - State state = channelState.getState(); - - if( state != null ) - { - switch( state ) - { - case CALL: - p = getGradient( mColorTopCall, mColorMiddleCall ); - break; - case CONTROL: - p = getGradient( mColorTopControl, mColorMiddleControl ); - break; - case DATA: - p = getGradient( mColorTopData, mColorMiddleData ); - break; - case FADE: - p = getGradient( mColorTopFade, mColorMiddleFade ); - break; - case NO_TUNER: - p = getGradient( mColorTopNoTuner, mColorMiddleNoTuner ); - break; - case END: - case IDLE: - default: - p = getGradient( mColorTopIdle, mColorMiddleIdle ); - break; - } - } - } - - g2.setPaint( p ); - g2.fillRect( 0, 0, getWidth(), getHeight() ); - - /* Draw bottom separator line */ - g2.setColor( Color.LIGHT_GRAY ); - g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 ); - - /* Draw channel selected box */ - if( mChannel.isSelected() ) - { - g2.setColor( mColorChannelSelected ); - g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 ); - } - - g2.dispose(); - } - - @Override - public void settingChanged( Setting setting ) - { - if( setting instanceof ColorSetting ) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch( colorSetting.getColorSettingName() ) - { - case CHANNEL_STATE_BACKGROUND: - mColorChannelBackground = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_CALL: - mColorMiddleCall = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_CALL: - mColorTopCall = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL: - mColorMiddleControl = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_CONTROL: - mColorTopControl = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_DATA: - mColorMiddleData = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_DATA: - mColorTopData = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_FADE: - mColorMiddleFade = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_FADE: - mColorTopFade = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE: - mColorMiddleIdle = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_IDLE: - mColorTopIdle = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER: - mColorMiddleNoTuner = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER: - mColorTopNoTuner = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_AUX_DECODER: - mColorLabelAuxDecoder = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_DECODER: - mColorLabelDecoder = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_DETAILS: - mColorLabelDetails = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_SELECTED_CHANNEL: - mColorChannelSelected = colorSetting.getColor(); - repaint(); - break; - } - } - } - - @Override - public void settingDeleted( Setting setting ) {} -} diff --git a/src/decode/DecodeEditorFactory.java b/src/decode/DecodeEditorFactory.java deleted file mode 100644 index 9d284a57..00000000 --- a/src/decode/DecodeEditorFactory.java +++ /dev/null @@ -1,63 +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 decode; - -import controller.channel.ChannelNode; -import decode.config.DecodeConfiguration; -import decode.ltrnet.LTRNetEditor; -import decode.ltrstandard.LTRStandardEditor; -import decode.mpt1327.MPT1327Editor; -import decode.nbfm.NBFMEditor; -import decode.p25.P25Editor; -import decode.passport.PassportEditor; - -public class DecodeEditorFactory -{ - public static DecodeEditor getPanel( DecodeConfiguration config, - ChannelNode channelNode ) - { - DecodeEditor configuredPanel; - - switch( config.getDecoderType() ) - { - case NBFM: - configuredPanel = new NBFMEditor( config ); - break; - case LTR_STANDARD: - configuredPanel = new LTRStandardEditor( config ); - break; - case LTR_NET: - configuredPanel = new LTRNetEditor( config ); - break; - case MPT1327: - configuredPanel = new MPT1327Editor( config, channelNode ); - break; - case PASSPORT: - configuredPanel = new PassportEditor( config ); - break; - case P25_PHASE1: - configuredPanel = new P25Editor( config ); - break; - default: - configuredPanel = new DecodeEditor( config ); - break; - } - - return configuredPanel; - } -} diff --git a/src/decode/Decoder.java b/src/decode/Decoder.java deleted file mode 100644 index 7fe365e4..00000000 --- a/src/decode/Decoder.java +++ /dev/null @@ -1,276 +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 decode; - -import java.util.ArrayList; -import java.util.List; - -import message.Message; -import sample.Broadcaster; -import sample.Listener; -import sample.complex.ComplexSample; -import sample.real.RealSampleBroadcaster; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import source.tuner.frequency.AutomaticFrequencyControl; -import source.tuner.frequency.FrequencyCorrectionControl; -import audio.IAudioOutput; -import decode.config.DecodeConfiguration; -import eventlog.MessageEventLogger; - -public abstract class Decoder implements Listener -{ - private MessageEventLogger mEventLogger; - private Broadcaster mMessageBroadcaster = new Broadcaster(); - private RealSampleBroadcaster mRealBroadcaster = new RealSampleBroadcaster(); - private Broadcaster mComplexBroadcaster = - new Broadcaster(); - - protected SampleType mSourceSampleType; - protected ArrayList mAuxiliaryDecoders = new ArrayList(); - - protected FrequencyCorrectionControl mFrequencyCorrection; - - /** - * Abstract decoder class. - * - * @param sampleType - complex (I/Q) or real (demodulated) sample type. - * Each decoder instance will be configured to receive either complex or - * real samples. When configured to receive complex samples, the - * decoder will implement a demodulator component and feed the output from - * that demodulator back onto itself via the real sample interface, so - * that any added auxiliary decoders will then receive the demodulated - * output stream. - */ - public Decoder( SampleType sampleType ) - { - mSourceSampleType = sampleType; - } - - public Decoder( DecodeConfiguration config, SampleType sampleType ) - { - this( sampleType ); - - if( config.supportsAFC() && config.isAFCEnabled() ) - { - mFrequencyCorrection = - new AutomaticFrequencyControl( config.getAFCMaximumCorrection() ); - - /* Register AFC to receive non-filtered demodulated audio samples */ - addUnfilteredRealSampleListener( - (RealSampleListener)mFrequencyCorrection ); - } - } - - /** - * Optional frequency correction control to provide frequency adjustments - * directed by the decoder. - */ - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return mFrequencyCorrection; - } - - /** - * Indicates if a frequency correction controller exists for this decoder - */ - public boolean hasFrequencyCorrectionControl() - { - return mFrequencyCorrection != null; - } - - /** - * Current frequency correction setting or zero if no controller exists - */ - public int getFrequencyCorrection() - { - if( hasFrequencyCorrectionControl() ) - { - return mFrequencyCorrection.getErrorCorrection(); - } - - return 0; - } - - /** - * Returns a real (ie demodulated) sample listener interface for - * connecting this decoder to a real sample stream provider - */ - public RealSampleListener getRealReceiver() - { - return mRealBroadcaster; - } - - public abstract IAudioOutput getAudioOutput(); - - /** - * Returns a complex listener interface for connecting this decoder to a - * float stream provider - */ - public Listener getComplexReceiver() - { - return (Listener)mComplexBroadcaster; - } - - /** - * Returns the primary decoder type for this decoder - */ - public abstract DecoderType getType(); - - /** - * Cleanup method. Invoke this method after stop and before delete. - */ - public void dispose() - { - for( Decoder auxiliaryDecoder: mAuxiliaryDecoders ) - { - auxiliaryDecoder.dispose(); - } - - mAuxiliaryDecoders.clear(); - - mMessageBroadcaster.clear(); - mComplexBroadcaster.clear(); - mRealBroadcaster.clear(); - - if( mFrequencyCorrection != null ) - { - mFrequencyCorrection.dispose(); - } - } - - /** - * Adds the auxiliary decoder (piggyback) to this decoder. - * - * Note: we assume that the auxiliary decoder is designed to receive - * demodulated samples, thus we automatically register the aux decoder to - * receive the demodulated sample stream. - * - * Registers this decoder to receive the message output stream from the - * auxiliary decoder, so that those messages can be echoed and included in - * the consolidated message stream to all message listeners registered on - * the primary decoder. - */ - public void addAuxiliaryDecoder( Decoder decoder ) - { - mAuxiliaryDecoders.add( decoder ); - mRealBroadcaster.addListener( decoder.getRealReceiver() ); - decoder.addMessageListener( this ); - } - - /** - * Returns a list of all auxiliary decoders that have been added to this - * processing chain - */ - public List getAuxiliaryDecoders() - { - return mAuxiliaryDecoders; - } - - /** - * Sets the MessageEventLogger for this processing chain - */ - public void setEventLogger( MessageEventLogger eventLogger ) - { - mEventLogger = eventLogger; - } - - /** - * Returns the MessageEventLogger for this processing chain - */ - public MessageEventLogger getEventLogger() - { - return mEventLogger; - } - - /** - * Main receiver method for all demodulators to send their decoded messages - * so that they will be broadcast to all registered listeners - */ - @Override - public void receive( Message message ) - { - send( message ); - } - - /** - * Broadcasts the message to all registered listeners - */ - public void send( Message message ) - { - if( mMessageBroadcaster != null ) - { - mMessageBroadcaster.receive( message ); - } - } - - /** - * Adds a listener to receiving decoded messages from all attached decoders - */ - public void addMessageListener( Listener listener ) - { - mMessageBroadcaster.addListener( listener ); - } - - /** - * Removes the listener from receiving decoded messages from all attached - * decoders - */ - public void removeMessageListener( Listener listener ) - { - mMessageBroadcaster.removeListener( listener ); - } - - /** - * Adds a real sample listener to receive unfiltered demodulated samples - */ - public abstract void addUnfilteredRealSampleListener( RealSampleListener listener ); - - /** - * Adds a real sample listener to receive demodulated samples - */ - public void addRealSampleListener( RealSampleListener listener ) - { - mRealBroadcaster.addListener( listener ); - } - - /** - * Remove the real sample listener from receiving demodulated samples - */ - public void removeRealListener( RealSampleListener listener ) - { - mRealBroadcaster.removeListener( listener ); - } - - /** - * Adds a complex (I/Q) sample listener to receive copy of the inbound - * complex sample stream - */ - public void addComplexListener( Listener listener ) - { - mComplexBroadcaster.addListener( listener ); - } - - /** - * Removes the complex (I/Q) sample listener from receiving samples - */ - public void removeComplexListener( Listener listener ) - { - mComplexBroadcaster.removeListener( listener ); - } -} diff --git a/src/decode/DecoderFactory.java b/src/decode/DecoderFactory.java deleted file mode 100644 index da7db700..00000000 --- a/src/decode/DecoderFactory.java +++ /dev/null @@ -1,219 +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 decode; - -import source.Source.SampleType; -import alias.AliasList; -import controller.channel.ChannelMap; -import controller.channel.ProcessingChain; -import controller.state.ChannelState; -import decode.am.AMChannelState; -import decode.am.AMDecoder; -import decode.config.AuxDecodeConfiguration; -import decode.config.DecodeConfigLTRNet; -import decode.config.DecodeConfigLTRStandard; -import decode.config.DecodeConfigMPT1327; -import decode.config.DecodeConfigP25Phase1; -import decode.config.DecodeConfiguration; -import decode.fleetsync2.Fleetsync2Decoder; -import decode.fleetsync2.FleetsyncChannelState; -import decode.lj1200.LJ1200ChannelState; -import decode.lj1200.LJ1200Decoder; -import decode.ltrnet.LTRNetChannelState; -import decode.ltrnet.LTRNetDecoder; -import decode.ltrstandard.LTRChannelState; -import decode.ltrstandard.LTRStandardDecoder; -import decode.mdc1200.MDCChannelState; -import decode.mdc1200.MDCDecoder; -import decode.mpt1327.MPT1327ChannelState; -import decode.mpt1327.MPT1327Decoder; -import decode.nbfm.NBFMChannelState; -import decode.nbfm.NBFMDecoder; -import decode.p25.P25ChannelState; -import decode.p25.P25Decoder; -import decode.passport.PassportChannelState; -import decode.passport.PassportDecoder; -import decode.tait.Tait1200ChannelState; -import decode.tait.Tait1200Decoder; - -public class DecoderFactory -{ - /** - * Returns a fully configured decoder with embedded auxiliary decoders - * @param channelConfig - * @return - */ - public static Decoder getDecoder( ProcessingChain chain, - SampleType sampleType, - AliasList aliasList ) - { - Decoder retVal = null; - - DecodeConfiguration config = - chain.getChannel().getDecodeConfiguration(); - - - switch( config.getDecoderType() ) - { - case AM: - retVal = new AMDecoder( sampleType ); - break; - case NBFM: - retVal = new NBFMDecoder( config, sampleType ); - break; - case LTR_STANDARD: - DecodeConfigLTRStandard ltrStandardConfig = - (DecodeConfigLTRStandard)config; - - retVal = new LTRStandardDecoder( config, sampleType, aliasList, - ltrStandardConfig.getMessageDirection() ); - break; - case LTR_NET: - DecodeConfigLTRNet ltrNetConfig = (DecodeConfigLTRNet)config; - - retVal = new LTRNetDecoder( config, sampleType, aliasList, - ltrNetConfig.getMessageDirection() ); - break; - case MPT1327: - DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)config; - - - retVal = new MPT1327Decoder( config, sampleType, aliasList, - mptConfig.getSync() ); - break; - case PASSPORT: - retVal = new PassportDecoder( config, sampleType, aliasList ); - break; - case P25_PHASE1: - DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)config; - - retVal = new P25Decoder( chain.getResourceManager(), - sampleType, p25Config.getModulation(), aliasList ); - break; - default: - throw new IllegalArgumentException( - "Unknown decoder type [" + - config.getDecoderType().toString() + "]" ); - } - - if( retVal != null ) - { - AuxDecodeConfiguration auxConfig = - chain.getChannel().getAuxDecodeConfiguration(); - - for( DecoderType auxDecoder: auxConfig.getAuxDecoders() ) - { - switch( auxDecoder ) - { - case FLEETSYNC2: - retVal.addAuxiliaryDecoder( - new Fleetsync2Decoder( aliasList ) ); - break; - case MDC1200: - retVal.addAuxiliaryDecoder( - new MDCDecoder( aliasList ) ); - break; - case LJ_1200: - retVal.addAuxiliaryDecoder( - new LJ1200Decoder( aliasList ) ); - break; - case TAIT_1200: - retVal.addAuxiliaryDecoder( - new Tait1200Decoder( aliasList ) ); - break; - } - } - } - - return retVal; - } - - public static ChannelState getChannelStateNew( ProcessingChain chain, - AliasList aliasList ) - { - ChannelState retVal = null; - - DecodeConfiguration decodeConfig = - chain.getChannel().getDecodeConfiguration(); - - switch( decodeConfig.getDecoderType() ) - { - case AM: - retVal = new AMChannelState( chain, aliasList ); - break; - case NBFM: - retVal = new NBFMChannelState( chain, aliasList ); - break; - case LTR_STANDARD: - retVal = new LTRChannelState( chain, aliasList ); - break; - case LTR_NET: - retVal = new LTRNetChannelState( chain, aliasList ); - break; - case MPT1327: - DecodeConfigMPT1327 mpt = (DecodeConfigMPT1327)decodeConfig; - - String channelMapName = mpt.getChannelMapName(); - - ChannelMap map = chain.getResourceManager().getPlaylistManager() - .getPlayist().getChannelMapList() - .getChannelMap( channelMapName ); - - retVal = new MPT1327ChannelState( chain, aliasList, map ); - break; - case PASSPORT: - retVal = new PassportChannelState( chain, aliasList ); - break; - case P25_PHASE1: - retVal = new P25ChannelState( chain, aliasList ); - break; - default: - break; - } - - if( retVal != null ) - { - AuxDecodeConfiguration auxConfig = - chain.getChannel().getAuxDecodeConfiguration(); - - for( DecoderType auxDecoder: auxConfig.getAuxDecoders() ) - { - switch( auxDecoder ) - { - case FLEETSYNC2: - retVal.addAuxChannelState( - new FleetsyncChannelState( retVal ) ); - break; - case LJ_1200: - retVal.addAuxChannelState( - new LJ1200ChannelState( retVal ) ); - break; - case MDC1200: - retVal.addAuxChannelState( - new MDCChannelState( retVal ) ); - break; - case TAIT_1200: - retVal.addAuxChannelState( - new Tait1200ChannelState( retVal ) ); - } - } - } - - return retVal; - } -} diff --git a/src/decode/am/AMChannelState.java b/src/decode/am/AMChannelState.java deleted file mode 100644 index 15a26a04..00000000 --- a/src/decode/am/AMChannelState.java +++ /dev/null @@ -1,69 +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 decode.am; - -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; - -/** - * Conventional channel state - this class doesn't do anything at the moment, - * it is a place holder for future expansion for the conventional decoder - */ -public class AMChannelState extends ChannelState -{ - public AMChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - } - - /** - * Make the ConventionalChannelState always unsquelched - */ - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - - super.setSquelchState( SquelchState.UNSQUELCH ); - } - - @Override - public void setSquelchState( SquelchState state ) - { - //do nothing ... we want the squelch state always unsquelched (for now) - } - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "\tDecoder:\tAM\n\n" ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } -} diff --git a/src/decode/am/AMDecoder.java b/src/decode/am/AMDecoder.java deleted file mode 100644 index 12acac7d..00000000 --- a/src/decode/am/AMDecoder.java +++ /dev/null @@ -1,105 +0,0 @@ -package decode.am; - -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import decode.Decoder; -import decode.DecoderType; -import dsp.am.AMDemodulator; -import dsp.filter.ComplexFIRFilter; -import dsp.filter.DCRemovalFilter2; -import dsp.filter.FilterFactory; -import dsp.filter.FloatFIRFilter; -import dsp.filter.Window.WindowType; -import dsp.gain.ComplexAutomaticGainControl; -import dsp.gain.RealAutomaticGainControl; - -public class AMDecoder extends Decoder -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double DC_REMOVAL_RATIO = 0.003; - - private ComplexFIRFilter mIQFilter; - private ComplexAutomaticGainControl mBasebandAGC = - new ComplexAutomaticGainControl(); - private AMDemodulator mDemodulator = new AMDemodulator(); - private DCRemovalFilter2 mDCRemovalFilter = - new DCRemovalFilter2( DC_REMOVAL_RATIO ); - private RealAutomaticGainControl mAudioAGC = new RealAutomaticGainControl(); - - private FloatFIRFilter mLowPassFilter; - - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "AM Decoder Audio Output" ); - - public AMDecoder( SampleType sampleType ) - { - super( sampleType ); - - /** - * Only setup a demod chain if we're receiving complex samples. If - * we're receiving demodulated samples, they'll be handled the same - * was as we handle the output of the demodulator. - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mIQFilter = new ComplexFIRFilter( - FilterFactory.getLowPass( 48000, 6500, 73, WindowType.HAMMING ), - 1.0 ); - /** - * The Decoder super class is both a Complex listener and a float - * listener. So, we add the demod to listen to the incoming - * quadrature samples, and we wire the output of the demod right - * back to this class, so we can receive the demodulated output - * to process - */ - this.addComplexListener( mIQFilter ); - mIQFilter.setListener( mBasebandAGC ); - mBasebandAGC.setListener( mDemodulator ); - - - /** - * Remove the DC component that is present when we're mistuned - */ - mDemodulator.setListener( mDCRemovalFilter ); - - /** - * Route the demodulated, filtered samples back to this class to send - * to all registered listeners - */ - - mLowPassFilter = new FloatFIRFilter( - FilterFactory.getLowPass( 48000, 3000, 31, WindowType.COSINE ), 1.0 ); - - mDCRemovalFilter.setListener( mLowPassFilter ); - - mLowPassFilter.setListener( mAudioAGC ); - mAudioAGC.setListener( this.getRealReceiver() ); - - addRealSampleListener( mAudioOutput ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } - - @Override - public DecoderType getType() - { - return DecoderType.AM; - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - throw new IllegalArgumentException( "cannot add real sample " - + "listener to AM demodulator - not implemented" ); - } - -} diff --git a/src/decode/am/AMPanel.java b/src/decode/am/AMPanel.java deleted file mode 100644 index 3842b807..00000000 --- a/src/decode/am/AMPanel.java +++ /dev/null @@ -1,136 +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 decode.am; - -import java.awt.EventQueue; - -import javax.swing.JLabel; - -import settings.ColorSetting; -import settings.Setting; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; - -public class AMPanel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - - private JLabel mProtocol = new JLabel( "AM" ); - - public AMPanel( Channel channel ) - { - super( channel ); - - init(); - } - - public void init() - { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - - mProtocol.setFont( mFontDecoder ); - mProtocol.setForeground( mColorLabelDecoder ); - - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - - add( mProtocol, "wrap" ); - } - - @Override - public void receive( final ChangedAttribute changedAttribute ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - switch( changedAttribute ) - { - case CHANNEL_STATE: - mStateLabel.setText( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - break; - case SOURCE: - mSourceLabel.setText( mChannel.getSourceConfiguration() - .getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; - } - - repaint(); - } - }); - } - - @Override - public void settingChanged( Setting setting ) - { - super.settingChanged( setting ); - - if( setting instanceof ColorSetting ) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch( colorSetting.getColorSettingName() ) - { - case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } - if( mProtocol != null ) - { - mProtocol.setForeground( mColorLabelDecoder ); - } - break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - break; - } - } - } -} diff --git a/src/decode/fleetsync2/FleetsyncActivitySummary.java b/src/decode/fleetsync2/FleetsyncActivitySummary.java deleted file mode 100644 index c1540651..00000000 --- a/src/decode/fleetsync2/FleetsyncActivitySummary.java +++ /dev/null @@ -1,171 +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 decode.fleetsync2; - -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class FleetsyncActivitySummary implements ActivitySummaryProvider, - Listener -{ - private AliasList mAliasList; - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public FleetsyncActivitySummary( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mIdents.clear(); - mEmergencyIdents.clear(); - } - - @Override - public void receive( Message message ) - { - if( message instanceof FleetsyncMessage ) - { - FleetsyncMessage fs = ((FleetsyncMessage)message); - - if( fs.isValid() ) - { - String from = fs.getFromID(); - mIdents.add( from ); - - String to = fs.getToID(); - - if( !to.startsWith( "xxx" )) - { - mIdents.add( to ); - } - } - - FleetsyncMessageType type = fs.getMessageType(); - - switch( type ) - { - case EMERGENCY: - case LONE_WORKER_EMERGENCY: - mEmergencyIdents.add( fs.getFromID() ); - break; - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tFleetsync II\n\n" ); - sb.append( "Fleetsync Idents\n" ); - - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( formatIdent( ident ) ); - sb.append( " " ); - - Alias alias = mAliasList.getFleetsyncAlias( ident ); - - if( alias != null ) - { - 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 ) ); - sb.append( " " ); - - Alias alias = mAliasList.getFleetsyncAlias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.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/decode/fleetsync2/FleetsyncChannelState.java b/src/decode/fleetsync2/FleetsyncChannelState.java deleted file mode 100644 index afcfc228..00000000 --- a/src/decode/fleetsync2/FleetsyncChannelState.java +++ /dev/null @@ -1,193 +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 decode.fleetsync2; - -import message.Message; -import alias.Alias; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; - -public class FleetsyncChannelState extends AuxChannelState -{ - private String mFleetFrom; - private Alias mFleetFromAlias; - - private String mFleetTo; - private Alias mFleetToAlias; - - private String mMessage; - private String mMessageType; - - private FleetsyncActivitySummary mActivitySummary; - - public FleetsyncChannelState( ChannelState parentState ) - { - super( parentState ); - - mActivitySummary = - new FleetsyncActivitySummary( parentState.getAliasList() ); - } - - @Override - public void reset() - { - setFleetIDFrom( null ); - setFleetIDFromAlias( null ); - setFleetIDTo( null ); - setFleetIDToAlias( null ); - setMessage( null ); - setMessageType( null ); - } - - @Override - public void fade() - { - } - - @Override - public void receive( Message message ) - { - if( message instanceof FleetsyncMessage ) - { - mActivitySummary.receive( message ); - - FleetsyncMessage fleetsync = (FleetsyncMessage)message; - - setFleetIDFrom( fleetsync.getFromID() ); - setFleetIDFromAlias( fleetsync.getFromIDAlias() ); - - FleetsyncMessageType type = fleetsync.getMessageType(); - - if( type != FleetsyncMessageType.ANI ) - { - setFleetIDTo( fleetsync.getToID() ); - setFleetIDToAlias( fleetsync.getToIDAlias() ); - } - - setMessageType( type.getLabel() ); - - switch( type ) - { - case GPS: - setMessage( fleetsync.getGPSLocation() ); - 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() ); - break; - default: - break; - } - - /* Set the state to CALL on the parent channel state, so that the - * parent state timer can track turning off any updates we apply */ - mParentChannelState.setState( State.CALL ); - - FleetsyncCallEvent fsCallEvent = - FleetsyncCallEvent.getFleetsync2Event( fleetsync ); - - fsCallEvent.setAliasList( mParentChannelState.getAliasList() ); - - mParentChannelState.receiveCallEvent( fsCallEvent ); - } - } - - public String getFleetIDFrom() - { - return mFleetFrom; - } - - public void setFleetIDFrom( String from ) - { - mFleetFrom = from; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public Alias getFleetIDFromAlias() - { - return mFleetFromAlias; - } - - public void setFleetIDFromAlias( Alias alias ) - { - mFleetFromAlias = alias; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } - - public String getFleetIDTo() - { - return mFleetTo; - } - - public void setFleetIDTo( String to ) - { - mFleetTo = to; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public Alias getFleetIDToAlias() - { - return mFleetToAlias; - } - - public void setFleetIDToAlias( Alias alias ) - { - mFleetToAlias = alias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage( String message ) - { - mMessage = message; - broadcastChange( ChangedAttribute.MESSAGE ); - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType( String type ) - { - mMessageType = type; - broadcastChange( ChangedAttribute.MESSAGE_TYPE ); - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } -} diff --git a/src/decode/lj1200/LJ1200ChannelState.java b/src/decode/lj1200/LJ1200ChannelState.java deleted file mode 100644 index 8bd626dc..00000000 --- a/src/decode/lj1200/LJ1200ChannelState.java +++ /dev/null @@ -1,67 +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 decode.lj1200; - -import message.Message; -import controller.state.AuxChannelState; -import controller.state.ChannelState; - -public class LJ1200ChannelState extends AuxChannelState -{ - private LJ1200ActivitySummary mActivitySummary; - - public LJ1200ChannelState( ChannelState parentState ) - { - super( parentState ); - - mActivitySummary = - new LJ1200ActivitySummary( parentState.getAliasList() ); - } - - @Override - public void reset() - { - } - - @Override - public void fade() - { - } - - @Override - public void receive( Message message ) - { - if( message instanceof LJ1200Message ) - { - mActivitySummary.receive( message ); - - mParentChannelState.receiveCallEvent( - LJ1200CallEvent.getLJ1200Event( (LJ1200Message)message ) ); - } - else if( message instanceof LJ1200TransponderMessage ) - { - mActivitySummary.receive( message ); - } - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } -} diff --git a/src/decode/lj1200/LJ1200MessageFilter.java b/src/decode/lj1200/LJ1200MessageFilter.java deleted file mode 100644 index 0c9a313e..00000000 --- a/src/decode/lj1200/LJ1200MessageFilter.java +++ /dev/null @@ -1,36 +0,0 @@ -package decode.lj1200; - -import java.util.Collections; -import java.util.List; - -import message.Message; -import filter.Filter; -import filter.FilterElement; - -public class LJ1200MessageFilter extends Filter -{ - - public LJ1200MessageFilter() - { - super( "LJ-1200 Message Filter" ); - } - - @Override - public boolean passes( Message message ) - { - return mEnabled && canProcess( message ); - } - - @Override - public boolean canProcess( Message message ) - { - return message instanceof LJ1200Message || - message instanceof LJ1200TransponderMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/decode/ltrnet/LTRNetActivitySummary.java b/src/decode/ltrnet/LTRNetActivitySummary.java deleted file mode 100644 index af4b0d85..00000000 --- a/src/decode/ltrnet/LTRNetActivitySummary.java +++ /dev/null @@ -1,404 +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 decode.ltrnet; - -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class LTRNetActivitySummary implements ActivitySummaryProvider -{ - private static final int sINT_NULL_VALUE = -1; - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private AliasList mAliasList; - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = 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 int mMonitoredLCN; - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public LTRNetActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof LTRNetOSWMessage ) - { - LTRNetOSWMessage ltr = ((LTRNetOSWMessage)message); - - if( ltr.isValid() ) - { - switch( ltr.getMessageType() ) - { - case CA_STRT: - case CA_ENDD: - String talkgroup = ltr.getTalkgroupID(); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - break; - case SY_IDLE: - mMonitoredLCN = ltr.getChannel(); - break; - case MA_CHNH: - break; - case MA_CHNL: - break; - case FQ_RXHI: - mReceiveFrequencies.put( ltr.getHomeRepeater(), - ltr.getFrequency() ); - break; - case FQ_RXLO: - break; - case FQ_TXHI: - mTransmitFrequencies.put( ltr.getHomeRepeater(), - ltr.getFrequency() ); - break; - case FQ_TXLO: - break; - case ID_ESNH: - break; - case ID_ESNL: - break; - case ID_NBOR: - String neighborID = ltr.getNeighborID(); - - if( neighborID != null ) - { - mNeighborIDs.add( neighborID ); - } - break; - case ID_UNIQ: - int uniqueid = ltr.getRadioUniqueID(); - - if( uniqueid != sINT_NULL_VALUE ) - { - mUniqueIDs.add( uniqueid ); - } - - break; - case ID_SITE: - String siteID = ltr.getSiteID(); - - if( siteID != null ) - { - mSiteIDs.add( siteID ); - } - break; - case UN_KNWN: - break; - default: - break; - } - } - } - else if( message instanceof LTRNetISWMessage ) - { - LTRNetISWMessage ltr = ((LTRNetISWMessage)message); - - if( ltr.isValid() ) - { - switch( ltr.getMessageType() ) - { - case CA_STRT: - case CA_ENDD: - String talkgroup = ltr.getTalkgroupID(); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - break; - case ID_ESNH: - case ID_ESNL: - String esn = ltr.getESN(); - - if( !esn.contains( "xxxx" ) ) - { - mESNs.add( ltr.getESN() ); - } - break; - case ID_UNIQ: - int uniqueid = ltr.getRadioUniqueID(); - - if( uniqueid != sINT_NULL_VALUE ) - { - mUniqueIDs.add( uniqueid ); - } - - break; - default: - break; - } - } - } - - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tLTR-Net\n\n" ); - - if( mSiteIDs.isEmpty() ) - { - sb.append( "Site:\tUnknown\n" ); - } - else - { - Iterator it = mSiteIDs.iterator(); - - while( it.hasNext() ) - { - sb.append( "Site:\t" ); - - String siteID = it.next(); - - sb.append( siteID ); - - Alias siteAlias = mAliasList.getSiteID( String.valueOf( siteID ) ); - - if( siteAlias != null ) - { - sb.append( " " ); - sb.append( siteAlias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nLCNs (transmit | receive)\n" ); - - if( mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - for( int x = 1; x < 21; x++ ) - { - double rcv = 0.0d; - - if( mReceiveFrequencies.containsKey( x ) ) - { - rcv = mReceiveFrequencies.get( x ); - } - - double xmt = 0.0d; - - if( mTransmitFrequencies.containsKey( x ) ) - { - xmt = mTransmitFrequencies.get( x ); - } - - if( rcv > 0.0d || xmt > 0.0d ) - { - if( x < 10 ) - { - sb.append( " " ); - } - - sb.append( x ); - sb.append( ": " ); - - if( xmt == -1.0d ) - { - sb.append( "---.-----" ); - } - else - { - sb.append( mDecimalFormatter.format( xmt ) ); - } - sb.append( " | " ); - - if( rcv == -1.0d ) - { - sb.append( "---.-----" ); - } - else - { - sb.append( mDecimalFormatter.format( rcv ) ); - } - - if( x == mMonitoredLCN ) - { - sb.append( " **" ); - } - - sb.append( "\n" ); - } - } - } - - sb.append( "\nTalkgroups\n" ); - - if( mTalkgroups.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while( it.hasNext() ) - { - String tgid = it.next(); - - sb.append( " " ); - sb.append( tgid ); - sb.append( " " ); - - Alias alias = mAliasList.getTalkgroupAlias( tgid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nRadio Unique IDs\n" ); - - if( mUniqueIDs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mUniqueIDs.iterator(); - - while( it.hasNext() ) - { - int uid = it.next(); - - sb.append( " " ); - sb.append( uid ); - sb.append( " " ); - - Alias alias = mAliasList.getUniqueID( uid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nESNs\n" ); - - if( mESNs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mESNs.iterator(); - - while( it.hasNext() ) - { - String esn = it.next(); - - sb.append( " " ); - sb.append( esn ); - sb.append( " " ); - - Alias alias = mAliasList.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( " " ); - - Alias alias = mAliasList.getSiteID( String.valueOf( neighbor ) ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/ltrnet/LTRNetChannelState.java b/src/decode/ltrnet/LTRNetChannelState.java deleted file mode 100644 index 5ed71f21..00000000 --- a/src/decode/ltrnet/LTRNetChannelState.java +++ /dev/null @@ -1,433 +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 decode.ltrnet; - -import java.awt.EventQueue; -import java.util.HashMap; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.activity.CallEventModel; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import controller.state.ChannelState.State; - -public class LTRNetChannelState extends ChannelState -{ - private String mTalkgroup; - private Alias mTalkgroupAlias; - private String mDescription; - private int mChannelNumber; - private HashMap mActiveCalls = new HashMap(); - private LTRNetActivitySummary mActivitySummary; - - public LTRNetChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - - mActivitySummary = new LTRNetActivitySummary( aliasList ); - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* - * We can receive multiple call tear-down messages -- only respond to - * the message that can change the state to fade - */ - if( getState().canChangeTo( State.FADE ) ) - { - LTRCallEvent current = getCurrentLTRCallEvent(); - - if( current != null ) - { - /* Close out the call event. If we only received 1 call - * message, then flag it as noise and remove the call event */ - if( current.isValid() ) - { - mCallEventModel.setEnd( current ); - } - else - { - mCallEventModel.remove( current ); - } - } - } - - setCurrentCallEvent( null ); - - mActiveCalls.clear(); - - super.fade( type ); - } - - public LTRCallEvent getCurrentLTRCallEvent() - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - return (LTRCallEvent)current; - } - - return null; - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof LTRNetOSWMessage ) - { - mActivitySummary.receive( message ); - - LTRNetOSWMessage ltr = (LTRNetOSWMessage)message; - - Alias talkgroupAlias = ltr.getTalkgroupIDAlias(); - - switch( ltr.getMessageType() ) - { - case CA_STRT: - if( mChannelNumber == 0 ) - { - mChannelNumber = ltr.getChannel(); - } - - if( ltr.getChannel() == mChannelNumber ) - { - processCallMessage( ltr ); - - if( ltr.getGroup() == 253 ) - { - mDescription = "REGISTER"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - } - else - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - } - else - { - //Call Detect - if( !mActiveCalls.containsKey( ltr.getChannel() ) || - !mActiveCalls.get( ltr.getChannel() ) - .contentEquals( ltr.getTalkgroupID() ) ) - { - mActiveCalls.put( ltr.getChannel(), - ltr.getTalkgroupID() ); - - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( String.valueOf( ltr.getChannel() ) ) - .to( ltr.getTalkgroupID() ) - .build() ); - } - } - break; - case CA_ENDD: - if( ltr.getGroup() != 253 ) - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - - fade( CallEventType.CALL_END ); - break; - case ID_UNIQ: - mDescription = "REGISTER UID"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - mTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - final LTRCallEvent currentEvent = getCurrentLTRCallEvent(); - - if( currentEvent != null && - currentEvent.getCallEventType() == CallEventType.REGISTER ) - { - final String uid = String.valueOf( ltr.getRadioUniqueID() ); - - if( uid != null ) - { - mCallEventModel.setFromID( currentEvent, uid ); - } - } - else - { - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "Radio Unique ID" ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .from( String.valueOf( ltr.getRadioUniqueID() ) ) - .build() ); - } - - setState( State.CALL ); - break; - case SY_IDLE: - mChannelNumber = ltr.getChannel(); - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - break; - default: - break; - } - - } - else if( message instanceof LTRNetISWMessage ) - { - mActivitySummary.receive( message ); - - LTRNetISWMessage ltr = (LTRNetISWMessage)message; - - Alias talkgroupAlias = ltr.getTalkgroupIDAlias(); - - switch( ltr.getMessageType() ) - { - case CA_STRT: - processCallMessage( ltr ); - - if( ltr.getGroup() == 253 ) - { - mDescription = "REGISTER"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - } - else - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - break; - case CA_ENDD: - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - fade( CallEventType.CALL_END ); - break; - case ID_UNIQ: - mDescription = "REGISTER UID"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - mTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - if( getState() != State.DATA ) - { - LTRCallEvent event = - new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .channel( String.valueOf( ltr.getChannel() ) ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( ltr.getTalkgroupID() ) - .build(); - - mCallEventModel.add( event ); - - setCurrentCallEvent( event ); - - setState( State.DATA ); - } - - if( mCurrentCallEvent != null ) - { - mCurrentCallEvent.setFromID( - String.valueOf( ltr.getRadioUniqueID() ) ); - mCurrentCallEvent.setDetails( "Unique ID" ); - } - break; - case ID_ESNH: - case ID_ESNL: - mDescription = "ESN"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - mTalkgroup = ltr.getESN(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = ltr.getESNAlias(); - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - setState( State.DATA ); - - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.REGISTER_ESN ) - .aliasList( mAliasList ) - .details( "ESN:" + ltr.getESN() ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .from( ltr.getESN() ) - .build() ); - break; - default: - break; - } - - } - } - - private void processCallMessage( LTRNetMessage message ) - { - LTRCallEvent current = getCurrentLTRCallEvent(); - - if( current == null || !current.addMessage( message ) ) - { - if( current != null ) - { - mCallEventModel.remove( current ); - } - - LTRCallEvent event; - - if( message.getGroup() == 253 ) - { - event = new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .channel( String.valueOf( message.getChannel() ) ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( message.getTalkgroupID() ) - .build(); - - setState( State.DATA ); - } - else - { - event = new LTRCallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( String.valueOf( message.getChannel() ) ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( message.getTalkgroupID() ) - .build(); - - setState( State.CALL ); - } - - mCallEventModel.add( event ); - - setCurrentCallEvent( event ); - } - else - { - if( getState() == State.CALL ) - { - setState( State.CALL ); - } - else if( getState() == State.DATA ) - { - setState( State.DATA ); - } - } - } - - public void reset() - { - mTalkgroup = null; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = null; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - mDescription = null; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - super.reset(); - } - - public String getToTalkgroup() - { - return mTalkgroup; - } - - public Alias getToTalkgroupAlias() - { - return mTalkgroupAlias; - } - - public String getDescription() - { - return mDescription; - } - - public boolean hasChannelNumber() - { - return mChannelNumber != 0; - } - - public int getChannelNumber() - { - return mChannelNumber; - } -} diff --git a/src/decode/ltrnet/LTRNetDecoder.java b/src/decode/ltrnet/LTRNetDecoder.java deleted file mode 100644 index 163573cc..00000000 --- a/src/decode/ltrnet/LTRNetDecoder.java +++ /dev/null @@ -1,175 +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 decode.ltrnet; - -import instrument.Instrumentable; -import instrument.tap.Tap; - -import java.util.ArrayList; -import java.util.List; - -import message.MessageDirection; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import bits.MessageFramer; -import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.fsk.LTRFSKDecoder; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class LTRNetDecoder extends Decoder implements Instrumentable -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - public static final int sLTR_STANDARD_MESSAGE_LENGTH = 40; - private FilteringNBFMDemodulator mDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDecoder mLTRFSKDecoder; - private MessageFramer mLTRMessageFramer; - private LTRNetMessageProcessor mLTRMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "LTRNet Decoder Audio Output" ); - - private List mAvailableTaps; - - public LTRNetDecoder( DecodeConfiguration config, - SampleType sampleType, - AliasList aliasList, - MessageDirection direction ) - { - super( config, sampleType ); - - /** - * Only setup a demod chain if we're receiving complex samples. If - * we're receiving demodulated samples, they'll be handled the same - * was as we handle the output of the demodulator. - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - /** - * The Decoder super class is both a Complex listener and a Float - * listener. So, we add the demod to listen to the incoming - * quadrature samples, and we wire the output of the demod right - * back to this class, so we can receive the demodulated output - * to process - */ - mDemodulator = new FilteringNBFMDemodulator(); - this.addComplexListener( mDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mDemodulator.addListener( mDCRemovalFilter ); - - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - addRealSampleListener( mAudioOutput ); - - mLTRFSKDecoder = new LTRFSKDecoder(); - - /** - * The DC removal filter will impact performance of the LTRFSKDemod, - * so we get the samples straight from the NBFM demod - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mDemodulator.addListener( mLTRFSKDecoder ); - } - else - { - this.addRealSampleListener( mLTRFSKDecoder ); - } - - if( direction == MessageDirection.OSW ) - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - else - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - - mLTRFSKDecoder.addListener( mLTRMessageFramer ); - - mLTRMessageProcessor = new LTRNetMessageProcessor( direction, aliasList ); - mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.addMessageListener( this ); - } - - @Override - public DecoderType getType() - { - return DecoderType.LTR_NET; - } - - @Override - public List getTaps() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - mAvailableTaps.addAll( mLTRFSKDecoder.getTaps() ); - } - - return mAvailableTaps; - } - - @Override - public void addTap( Tap tap ) - { - mLTRFSKDecoder.addTap( tap ); - } - - @Override - public void removeTap( Tap tap ) - { - mLTRFSKDecoder.removeTap( tap ); - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mDemodulator != null ) - { - mDemodulator.addListener( listener ); - } - // TODO Auto-generated method stub - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/ltrnet/LTRNetMessageProcessor.java b/src/decode/ltrnet/LTRNetMessageProcessor.java deleted file mode 100644 index 7a7dcc19..00000000 --- a/src/decode/ltrnet/LTRNetMessageProcessor.java +++ /dev/null @@ -1,165 +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 decode.ltrnet; - -import java.util.HashMap; - -import message.Message; -import message.MessageDirection; -import message.MessageType; -import sample.Broadcaster; -import sample.Listener; -import alias.AliasList; -import bits.BinaryMessage; - -public class LTRNetMessageProcessor implements Listener -{ - private Broadcaster mBroadcaster = new Broadcaster(); - - private HashMap mReceiveHighAuxMessages = - new HashMap(); - private HashMap mReceiveLowAuxMessages = - new HashMap(); - - private HashMap mTransmitHighAuxMessages = - new HashMap(); - private HashMap mTransmitLowAuxMessages = - new HashMap(); - - 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 ) - { - 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; - } - } - } - } - } - - mBroadcaster.receive( message ); - } - - public void addMessageListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeMessageListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } -} diff --git a/src/decode/ltrstandard/LTRChannelState.java b/src/decode/ltrstandard/LTRChannelState.java deleted file mode 100644 index 5563b5bf..00000000 --- a/src/decode/ltrstandard/LTRChannelState.java +++ /dev/null @@ -1,240 +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 decode.ltrstandard; - -import java.util.HashMap; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.activity.CallEventModel; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import decode.ltrnet.LTRCallEvent; - -public class LTRChannelState extends ChannelState -{ - private String mTalkgroup; - private Alias mTalkgroupAlias; - private int mChannelNumber; - private HashMap mActiveCalls = new HashMap(); - private LTRStandardActivitySummary mActivitySummary; - - public LTRChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - - mActivitySummary = new LTRStandardActivitySummary( aliasList ); - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof LTRStandardOSWMessage ) - { - mActivitySummary.receive( message ); - - LTRStandardOSWMessage ltr = (LTRStandardOSWMessage)message; - - switch( ltr.getMessageType() ) - { - /** - * Process Call Message - * - * States: - * 1) Call message with invalid LCN - * 2) State=IDLE/FADE, new CA_STRT - * 3) State=CALL, new CA_STRT, same talkgroup - * 4) State=CALL, new CA_STRT, different talkgroup - */ - case CA_STRT: - if( 0 < ltr.getChannel() && ltr.getChannel() <=20 ) - { - /* If we haven't captured the LCN yet ... */ - if( mChannelNumber == 0 ) - { - mChannelNumber = ltr.getChannel(); - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - - /* If this call event is for this channel, then update - * talkgroup and alias */ - if( ltr.getChannel() == mChannelNumber ) - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mTalkgroupAlias = ltr.getTalkgroupIDAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - LTRCallEvent current = getCurrentLTRCallEvent(); - - /* If we have an ongoing call, then add this message - * to the current event - if addMessage returns - * false, then we have a different talkgroup and a - * new call event, so close out the old one. */ - if( current != null && !current.addMessage( ltr ) ) - { - setState( State.FADE ); - } - - if( current == null ) - { - LTRCallEvent event = - new LTRCallEvent.Builder( CallEventType.CALL ) - .to( ltr.getTalkgroupID() ) - .aliasList( getAliasList() ) - .channel( String.valueOf( ltr.getChannel() ) ) - .frequency( ( ltr.getChannel() == mChannelNumber ) ? - mProcessingChain.getChannel() - .getTunerChannel().getFrequency() : 0 ) - .build(); - - mCallEventModel.add( event ); - - setCurrentCallEvent( event ); - } - - setState( State.CALL ); - } - } - break; - case CA_ENDD: - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mTalkgroupAlias = ltr.getTalkgroupIDAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - fade( CallEventType.CALL_END ); - break; - case SY_IDLE: - int channel = ltr.getChannel(); - - if( 0 < channel && channel <= 20 && - channel == ltr.getHomeRepeater() && - channel == ltr.getFree() ) - { - mChannelNumber = channel; - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - break; - default: - break; - } - } - } - - public LTRCallEvent getCurrentLTRCallEvent() - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - return (LTRCallEvent)current; - } - - return null; - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* We can receive multiple call tear-down messages -- only respond to - * the message that can change the state to fade */ - if( getState().canChangeTo( State.FADE ) ) - { - mActiveCalls.clear(); - - LTRCallEvent current = getCurrentLTRCallEvent(); - - if( current != null ) - { - /* Close out the call event. If we only received 1 call - * message, then flag it as noise and remove the call event */ - if( current.isValid() ) - { - mCallEventModel.setEnd( current ); - } - else - { - mCallEventModel.remove( current ); - } - } - - setCurrentCallEvent( null ); - } - - super.fade( type ); - } - - public void reset() - { - mTalkgroup = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mTalkgroupAlias = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - super.reset(); - } - - public String getTalkgroup() - { - return mTalkgroup; - } - - public Alias getTalkgroupAlias() - { - return mTalkgroupAlias; - } - - public boolean hasChannelNumber() - { - return mChannelNumber != 0; - } - - public int getChannelNumber() - { - return mChannelNumber; - } -} diff --git a/src/decode/ltrstandard/LTRStandardActivitySummary.java b/src/decode/ltrstandard/LTRStandardActivitySummary.java deleted file mode 100644 index cee0a23e..00000000 --- a/src/decode/ltrstandard/LTRStandardActivitySummary.java +++ /dev/null @@ -1,209 +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 decode.ltrstandard; - -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class LTRStandardActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private AliasList mAliasList; - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mActiveLCNs = new TreeSet(); - private int mMonitoredLCN; - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public LTRStandardActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof LTRStandardOSWMessage ) - { - LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message); - - if( ltr.isValid() ) - { - switch( ltr.getMessageType() ) - { - case CA_STRT: - case CA_ENDD: - int home = ltr.getHomeRepeater(); - - if( 0 < home && home <= 20 ) - { - String talkgroup = ltr.getTalkgroupID( false ); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - } - - int channel = ltr.getChannel(); - - if( 0 < channel && channel <= 20 ) - { - if( channel == home ) - { - mMonitoredLCN = channel; - mActiveLCNs.add( channel ); - } - - int free = ltr.getFree(); - - if( 0 < free && free <= 20 ) - { - mActiveLCNs.add( free ); - } - } - - break; - case SY_IDLE: - int idleChannel = ltr.getChannel(); - - if( idleChannel == ltr.getHomeRepeater() && - idleChannel == ltr.getFree() ) - { - mMonitoredLCN = idleChannel; - mActiveLCNs.add( idleChannel ); - } - break; - case UN_KNWN: - default: - break; - } - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n\n" ); - - sb.append( "Decoder:\tLTR-Standard\n" ); - - sb.append( "Monitored LCN: " ); - - if( mMonitoredLCN > 0 ) - { - sb.append( mMonitoredLCN ); - } - else - { - sb.append( "unknown" ); - } - - sb.append( "\n" ); - - Integer[] lcns = mActiveLCNs.toArray( new Integer[0] ); - - sb.append( "Active LCNs:\t" ); - - if( lcns.length > 0 ) - { - sb.append( Arrays.toString( lcns ) ); - } - else - { - sb.append( "none" ); - } - sb.append( "\n\n" ); - - sb.append( "Talkgroups\n" ); - - if( mTalkgroups.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while( it.hasNext() ) - { - String tgid = it.next(); - - sb.append( " " ); - sb.append( formatTalkgroup( tgid ) ); - sb.append( " " ); - - Alias alias = mAliasList.getTalkgroupAlias( tgid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } - - 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; - } - } -} diff --git a/src/decode/ltrstandard/LTRStandardDecoder.java b/src/decode/ltrstandard/LTRStandardDecoder.java deleted file mode 100644 index 338e7003..00000000 --- a/src/decode/ltrstandard/LTRStandardDecoder.java +++ /dev/null @@ -1,141 +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 decode.ltrstandard; - -import message.MessageDirection; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import bits.MessageFramer; -import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.fsk.LTRFSKDecoder; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class LTRStandardDecoder extends Decoder -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - public static final int sLTR_STANDARD_MESSAGE_LENGTH = 40; - private FilteringNBFMDemodulator mDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDecoder mLTRFSKDecoder; - private MessageFramer mLTRMessageFramer; - private LTRStandardMessageProcessor mLTRMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "LTR Standard Decoder Audio Output" ); - - public LTRStandardDecoder( DecodeConfiguration config, - SampleType sampleType, - AliasList aliasList, - MessageDirection direction ) - { - super( config, sampleType ); - - /** - * Only setup a demod chain if we're receiving complex samples. If - * we're receiving demodulated samples, they'll be handled the same - * was as we handle the output of the demodulator. - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - /** - * The Decoder super class is both a Complex listener and a Short - * listener. So, we add the demod to listen to the incoming - * quadrature samples, and we wire the output of the demod right - * back to this class, so we can receive the demodulated output - * to process - */ - mDemodulator = new FilteringNBFMDemodulator(); - this.addComplexListener( mDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mDemodulator.addListener( mDCRemovalFilter ); - - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - addRealSampleListener( mAudioOutput ); - - mLTRFSKDecoder = new LTRFSKDecoder(); - - /** - * The DC removal filter will impact performance of the LTRFSKDemod, - * so we get the samples straight from the NBFM demod - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mDemodulator.addListener( mLTRFSKDecoder ); - } - else - { - this.addRealSampleListener( mLTRFSKDecoder ); - } - - if( direction == MessageDirection.OSW ) - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - else - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - - mLTRFSKDecoder.addListener( mLTRMessageFramer ); - - mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); - mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.addMessageListener( this ); - } - - @Override - public DecoderType getType() - { - return DecoderType.LTR_STANDARD; - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mDemodulator != null ) - { - mDemodulator.addListener( listener ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/mdc1200/MDCActivitySummary.java b/src/decode/mdc1200/MDCActivitySummary.java deleted file mode 100644 index f937ad91..00000000 --- a/src/decode/mdc1200/MDCActivitySummary.java +++ /dev/null @@ -1,131 +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 decode.mdc1200; - -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class MDCActivitySummary implements ActivitySummaryProvider, - Listener -{ - private AliasList mAliasList; - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public MDCActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof MDCMessage ) - { - MDCMessage mdc = ((MDCMessage)message); - - mIdents.add( mdc.getUnitID() ); - - if( mdc.isEmergency() ) - { - mEmergencyIdents.add( mdc.getUnitID() ); - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tMDC-1200\n\n" ); - sb.append( "MDC-1200 Idents\n" ); - - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( ident ); - sb.append( " " ); - - Alias alias = mAliasList.getMDC1200Alias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "MDC-1200 Emergency Idents\n" ); - - if( mEmergencyIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( ident ); - sb.append( " " ); - - Alias alias = mAliasList.getMDC1200Alias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/mdc1200/MDCChannelState.java b/src/decode/mdc1200/MDCChannelState.java deleted file mode 100644 index b73c9d9a..00000000 --- a/src/decode/mdc1200/MDCChannelState.java +++ /dev/null @@ -1,183 +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 decode.mdc1200; - -import message.Message; -import alias.Alias; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; - -public class MDCChannelState extends AuxChannelState -{ - private String mFrom; - private Alias mFromAlias; - private String mTo; - private Alias mToAlias; - private String mMessage; - private String mMessageType; - - private MDCActivitySummary mActivitySummary; - - public MDCChannelState( ChannelState parentState ) - { - super( parentState ); - - mActivitySummary = new MDCActivitySummary( parentState.getAliasList() ); - } - - @Override - public void reset() - { - setFrom( null ); - setFromAlias( null ); - setTo( null); - setToAlias( null ); - setMessage( null ); - setMessageType( null ); - } - - @Override - public void fade() {} - - @Override - public void receive( Message message ) - { - if( message instanceof MDCMessage ) - { - mActivitySummary.receive( message ); - - MDCMessage mdc = (MDCMessage)message; - - setFrom( mdc.getFromID() ); - setFromAlias( mdc.getFromIDAlias() ); - - MDCMessageType type = mdc.getMessageType(); - - setMessageType( type.getLabel() ); - - StringBuilder sb = new StringBuilder(); - - switch( type ) - { - case ACKNOWLEDGE: - case ANI: - case EMERGENCY: - case PAGING: - case STATUS: - default: - sb.append( "OPCODE " ); - sb.append( String.valueOf( mdc.getOpcode() ) ); - - if( mdc.isBOT() ) - { - sb.append( " TYPE:BOT" ); - } - - if( mdc.isEOT() ) - { - sb.append( " TYPE:EOT" ); - } - break; - } - - setMessage( sb.toString() ); - - //Set the state to CALL on the parent channel state, so that the - //timer can track turning off any updates we apply - mParentChannelState.setState( State.CALL ); - - MDCCallEvent event = MDCCallEvent.getMDCCallEvent( mdc ); - event.setAliasList( mParentChannelState.getAliasList() ); - mParentChannelState.receiveCallEvent( event ); - } - } - - public String getFrom() - { - return mFrom; - } - - public void setFrom( String from ) - { - mFrom = from; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public Alias getFromAlias() - { - return mFromAlias; - } - - public void setFromAlias( Alias alias ) - { - mFromAlias = alias; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } - - public String getTo() - { - return mTo; - } - - public void setTo( String to ) - { - mTo = to; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public Alias getToAlias() - { - return mToAlias; - } - - public void setToAlias( Alias alias ) - { - mToAlias = alias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage( String message ) - { - mMessage = message; - broadcastChange( ChangedAttribute.MESSAGE ); - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType( String type ) - { - mMessageType = type; - broadcastChange( ChangedAttribute.MESSAGE_TYPE ); - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } -} diff --git a/src/decode/mpt1327/MPT1327ActivitySummary.java b/src/decode/mpt1327/MPT1327ActivitySummary.java deleted file mode 100644 index 018fd9f7..00000000 --- a/src/decode/mpt1327/MPT1327ActivitySummary.java +++ /dev/null @@ -1,159 +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 decode.mpt1327; - -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class MPT1327ActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - - private AliasList mAliasList; - private TreeSet mIdents = new TreeSet(); - private String mSite; - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public MPT1327ActivitySummary( AliasList list ) - { - mAliasList = list; - } - - /** - * Cleanup method - */ - public void dispose() - { - mIdents.clear(); - } - - @Override - public void receive( Message message ) - { - if( message instanceof MPT1327Message ) - { - MPT1327Message mpt = ((MPT1327Message)message); - - if( mpt.isValid() ) - { - switch( mpt.getMessageType() ) - { - case ACK: - mIdents.add( mpt.getFromID() ); - break; - case ACKI: - mIdents.add( mpt.getFromID() ); - mIdents.add( mpt.getToID() ); - break; - case AHYC: - mIdents.add( mpt.getToID() ); - break; - case ALH: - mSite = mpt.getSiteID(); - break; - case GTC: - if( mpt.hasFromID() ) - { - mIdents.add( mpt.getFromID() ); - } - if( mpt.hasToID() ) - { - mIdents.add( mpt.getToID() ); - } - break; - } - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tMPT-1327\n" ); - sb.append( "Site:\t" ); - - if( mSite != null ) - { - sb.append( mSite ); - - if( mAliasList != null ) - { - Alias siteAlias = mAliasList.getSiteID( mSite ); - - if( siteAlias != null ) - { - sb.append( " " ); - sb.append( siteAlias.getName() ); - sb.append( "\n" ); - } - } - } - else - { - sb.append( "Unknown\n" ); - } - - sb.append( "\n" ); - - sb.append( "Talkgroups\n" ); - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( ident ); - sb.append( " " ); - - Alias alias = mAliasList.getMPT1327Alias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/mpt1327/MPT1327ChannelState.java b/src/decode/mpt1327/MPT1327ChannelState.java deleted file mode 100644 index 5b3268ee..00000000 --- a/src/decode/mpt1327/MPT1327ChannelState.java +++ /dev/null @@ -1,422 +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 decode.mpt1327; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import message.Message; -import sample.Listener; -import source.config.SourceConfigTuner; -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.channel.Channel; -import controller.channel.Channel.ChannelType; -import controller.channel.ChannelMap; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import decode.config.DecodeConfigMPT1327; -import decode.mpt1327.MPT1327Message.IdentType; - -public class MPT1327ChannelState extends ChannelState -{ - private final static Logger mLog = LoggerFactory.getLogger( MPT1327ChannelState.class ); - - private String mSite; - private String mFromTalkgroup; - private String mToTalkgroup; - private int mChannelNumber; - private ChannelType mChannelType; - private ChannelMap mChannelMap; - private MPT1327ActivitySummary mActivitySummary; - - public MPT1327ChannelState( ProcessingChain processingChain, - AliasList aliasList, - ChannelMap channelMap ) - { - super( processingChain, aliasList ); - - mActivitySummary = new MPT1327ActivitySummary( aliasList ); - - mChannelMap = channelMap; - - mChannelType = processingChain.getChannel().getChannelType(); - } - - public void dispose() - { - super.dispose(); - - mActivitySummary.dispose(); - mActivitySummary = null; - mCurrentCallEvent = null; - } - - public void setCurrentCall( MPT1327CallEvent event ) - { - mCurrentCallEvent = event; - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof MPT1327Message ) - { - mActivitySummary.receive( message ); - - MPT1327Message mpt = (MPT1327Message)message; - - if( mpt.isValid() ) - { - switch( mpt.getMessageType() ) - { - case ACK: - IdentType identType = ( (MPT1327Message) message ).getIdent1Type(); - - if( identType == IdentType.REGI ) - { - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( "REGISTERED ON NETWORK" ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getToID() ) - .to( mpt.getFromID() ) - .build() ); - } - else - { - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( "ACK " + identType.getLabel() ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - } - break; - case AHYC: - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( ( (MPT1327Message) message ).getRequestString() ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - break; - case AHYQ: - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.STATUS ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( mpt.getStatusMessage() ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - break; - case ALH: - String site = mpt.getSiteID(); - - if( mSite == null ) - { - mSite = site; - broadcastChange( ChangedAttribute.CHANNEL_SITE_NUMBER ); - } - else if( site != null && !site.contentEquals( mSite ) ) - { - mSite = site; - broadcastChange( ChangedAttribute.CHANNEL_SITE_NUMBER ); - } - - setState( State.CONTROL ); - break; - case GTC: - if( mpt.isValidCall() ) - { - int channelNumber = mpt.getChannel(); - - if( !getProcessingChain().getChannel() - .hasTrafficChannel( channelNumber ) ) - { - String aliasListName = mProcessingChain - .getChannel().getAliasListName(); - - Channel traffic = getTrafficChannel( channelNumber, - aliasListName ); - - /* Set the system and site to same as control channel */ - traffic.setSystem( getProcessingChain().getChannel() - .getSystem(), false ); - traffic.setSite( getProcessingChain().getChannel() - .getSite(), false ); - - /* Add the traffic channel to the parent control channel */ - getProcessingChain().getChannel() - .addTrafficChannel( channelNumber, traffic ); - - /* Start the traffic channel */ - traffic.setEnabled( true ); - - /* Set traffic channel state info */ - MPT1327ChannelState trafficState = - (MPT1327ChannelState)traffic - .getProcessingChain().getChannelState(); - - MPT1327ChannelState controlState = - (MPT1327ChannelState)getProcessingChain() - .getChannelState(); - - trafficState.setChannelMap( controlState.getChannelMap() ); - trafficState.setChannelNumber( channelNumber ); - trafficState.setFromTalkgroup( mpt.getFromID() ); - trafficState.setToTalkgroup( mpt.getToID() ); - - /* Add this control channel as message listener on the - * traffic channel so we can receive call tear-down - * messages */ - traffic.addListener( (Listener)this ); - - CallEventType type = traffic.isProcessing() ? - CallEventType.CALL : - CallEventType.CALL_NO_TUNER; - - /* - * Set the traffic channel's call event model to - * share the control channel's call event model - */ - traffic.getProcessingChain().getChannelState() - .setCallEventModel( mCallEventModel ); - - MPT1327CallEvent callStartEvent = - new MPT1327CallEvent.Builder( type ) - .aliasList( traffic.getProcessingChain().getAliasList() ) - .channel( String.valueOf( channelNumber ) ) - .frequency( mChannelMap.getFrequency( channelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build(); - - traffic.getProcessingChain().getChannelState() - .setCurrentCallEvent( callStartEvent ); - - mCallEventModel.add( callStartEvent ); - } - } - break; - case CLEAR: - case MAINT: - if( mChannelType == ChannelType.TRAFFIC ) - { - fade( CallEventType.CALL_END ); - } - break; - case HEAD_PLUS1: - case HEAD_PLUS2: - case HEAD_PLUS3: - case HEAD_PLUS4: - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .details( mpt.getMessage() ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - break; - } - } - } - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* - * We can receive multiple call tear-down messages -- only post a call - * end event for the one that can change the state to fade - */ - if( getState().canChangeTo( State.FADE ) ) - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - mCallEventModel.setEnd( current ); - } - else - { - mCallEventModel.add( - new MPT1327CallEvent.Builder( type ) - .aliasList( getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mFromTalkgroup ) - .to( mToTalkgroup ) - .build() ); - } - - setCurrentCall( null ); - } - - super.fade( type ); - } - - /** - * Make the ConventionalChannelState always unsquelched - */ - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - - super.setSquelchState( SquelchState.UNSQUELCH ); - } - - public void reset() - { - mFromTalkgroup = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - mToTalkgroup = null; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - super.reset(); - } - - public String getSite() - { - return mSite; - } - - public ChannelMap getChannelMap() - { - return mChannelMap; - } - - public void setChannelMap( ChannelMap channelMap ) - { - mChannelMap = channelMap; - } - - public String getFromTalkgroup() - { - return mFromTalkgroup; - } - - /** - * Set the talkgroup. This is used primarily for traffic channels since - * the talkgroup will already have been identified prior to the traffic - * channel being created. - */ - public void setFromTalkgroup( String fromTalkgroup ) - { - mFromTalkgroup = fromTalkgroup; - - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public String getToTalkgroup() - { - return mToTalkgroup; - } - - /** - * Set the talkgroup. This is used primarily for traffic channels since - * the talkgroup will already have been identified prior to the traffic - * channel being created. - */ - public void setToTalkgroup( String toTalkgroup ) - { - mToTalkgroup = toTalkgroup; - - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public int getChannelNumber() - { - return mChannelNumber; - } - /** - * Set the channel number. This is used primarily for traffic channels since - * the channel will already have been identified prior to the traffic - * channel being created. - */ - public void setChannelNumber( int channel ) - { - mChannelNumber = channel; - - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - - public String getAliasListName() - { - return mProcessingChain.getChannel().getAliasListName(); - } - - private Channel getTrafficChannel( int channelNumber, String aliasListName ) - { - Channel traffic = new Channel( "Traffic Channel " + channelNumber, - ChannelType.TRAFFIC ); - - traffic.setResourceManager( getProcessingChain().getResourceManager() ); - - SourceConfigTuner source = new SourceConfigTuner(); - - long frequency = mChannelMap.getFrequency( channelNumber ); - - source.setFrequency( frequency ); - - traffic.setSourceConfiguration( source ); - - DecodeConfigMPT1327 decode = new DecodeConfigMPT1327(); - - traffic.setDecodeConfiguration( decode ); - - traffic.setAliasListName( aliasListName ); - - return traffic; - } -} diff --git a/src/decode/mpt1327/MPT1327Panel.java b/src/decode/mpt1327/MPT1327Panel.java deleted file mode 100644 index 9cb89284..00000000 --- a/src/decode/mpt1327/MPT1327Panel.java +++ /dev/null @@ -1,282 +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 decode.mpt1327; - -import java.awt.EventQueue; - -import javax.swing.ImageIcon; -import javax.swing.JLabel; - -import settings.ColorSetting; -import settings.Setting; -import alias.Alias; -import alias.AliasList; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; - -public class MPT1327Panel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mProtocol; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - - private JLabel mStateLabel; - private JLabel mTalkgroup; - private JLabel mTalkgroupAlias; - - private JLabel mSiteLabel; - private JLabel mSite; - private JLabel mSiteAliasLabel; - - private MPT1327ChannelState mState; - - private AliasList mAliasList; - - public MPT1327Panel( Channel channel ) - { - super( channel ); - mState = (MPT1327ChannelState)channel.getProcessingChain().getChannelState(); - mState.addListener( this ); - - mAliasList = mState.getAliasList(); - - init(); - } - - public void dispose() - { - super.dispose(); - mState = null; - mAliasList = null; - } - - public void init() - { - mProtocol = new JLabel( "MPT1327" ); - mProtocol.setFont( mFontDecoder ); - mProtocol.setForeground( mColorLabelDecoder ); - - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mTalkgroup = new JLabel( mState.getFromTalkgroup() ); - mTalkgroup.setFont( mFontDecoder ); - mTalkgroup.setForeground( mColorLabelDecoder ); - - mTalkgroupAlias = new JLabel(); - mTalkgroupAlias.setFont( mFontDecoder ); - mTalkgroupAlias.setForeground( mColorLabelDecoder ); - - Alias alias = getTalkgroupAlias( mState.getFromTalkgroup() ); - - if( alias != null ) - { - mTalkgroupAlias.setText( alias.getName() ); - mTalkgroupAlias.setIcon( getIcon( alias ) ); - } - - mSiteLabel = new JLabel( "Site:" ); - mSiteLabel.setFont( mFontDetails ); - mSiteLabel.setForeground( mColorLabelDetails ); - - mSite = new JLabel( String.valueOf( mState.getSite() ) ); - mSite.setFont( mFontDecoder ); - mSite.setForeground( mColorLabelDecoder ); - - mSiteAliasLabel = new JLabel(); - mSiteAliasLabel.setFont( mFontDecoder ); - mSiteAliasLabel.setForeground( mColorLabelDecoder ); - - Alias siteAlias = getSiteAlias( mState.getSite() ); - - if( siteAlias != null ) - { - mSiteAliasLabel.setText( siteAlias.getName() ); - } - - add( mProtocol ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - - add( mStateLabel ); - add( mTalkgroup ); - add( mTalkgroupAlias, "wrap" ); - - add( mSiteLabel ); - add( mSite ); - add( mSiteAliasLabel, "wrap" ); - } - - @Override - public void receive( final ChangedAttribute changedAttribute ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - switch( changedAttribute ) - { - case CHANNEL_STATE: - mStateLabel.setText( mState.getState().getDisplayValue() ); - break; - case CHANNEL_SITE_NUMBER: - mSite.setText( String.valueOf( mState.getSite() ) ); - - Alias siteAlias = getSiteAlias( mState.getSite() ); - - if( siteAlias != null ) - { - mSiteAliasLabel.setText( siteAlias.getName() ); - } - repaint(); - break; - case FROM_TALKGROUP: - mTalkgroup.setText( mState.getFromTalkgroup() ); - - Alias alias = getTalkgroupAlias( mState.getFromTalkgroup() ); - - if( alias != null ) - { - mTalkgroupAlias.setText( alias.getName() ); - mTalkgroupAlias.setIcon( getIcon( alias ) ); - } - repaint(); - break; - } - } - } ); - } - - private ImageIcon getIcon( Alias alias ) - { - if( alias != null ) - { - String iconName = alias.getIconName(); - - if( iconName != null ) - { - return getSettingsManager().getImageIcon( iconName, 12 ); - } - } - - return null; - } - - private Alias getSiteAlias( String site ) - { - if( site != null && mAliasList != null ) - { - return mAliasList.getSiteID( site ); - } - - return null; - } - - private Alias getTalkgroupAlias( String talkgroup ) - { - if( talkgroup != null && mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( talkgroup ); - } - - return null; - } - - @Override - public void trafficChannelAdded( Channel traffic ) - { - if( !getTrafficPanels().keySet().contains( traffic ) ) - { - MPT1327TrafficPanel panel = - new MPT1327TrafficPanel( traffic ); - - getTrafficPanels().put( traffic, panel ); - - addPanel( panel ); - } - } - - @Override - public void settingChanged( Setting setting ) - { - super.settingChanged( setting ); - - if( setting instanceof ColorSetting ) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch( colorSetting.getColorSettingName() ) - { - case CHANNEL_STATE_LABEL_DECODER: - if( mProtocol != null ) - { - mProtocol.setForeground( mColorLabelDecoder ); - } - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } - if( mTalkgroup != null ) - { - mTalkgroup.setForeground( mColorLabelDecoder ); - } - if( mTalkgroupAlias != null ) - { - mTalkgroupAlias.setForeground( mColorLabelDecoder ); - } - if( mSite != null ) - { - mSite.setForeground( mColorLabelDecoder ); - } - if( mSiteAliasLabel != null ) - { - mSiteAliasLabel.setForeground( mColorLabelDecoder ); - } - break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - if( mSiteLabel != null ) - { - mSiteLabel.setForeground( mColorLabelDetails ); - } - break; - } - } - } -} diff --git a/src/decode/mpt1327/MPT1327TrafficPanel.java b/src/decode/mpt1327/MPT1327TrafficPanel.java deleted file mode 100644 index e2647d04..00000000 --- a/src/decode/mpt1327/MPT1327TrafficPanel.java +++ /dev/null @@ -1,258 +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 decode.mpt1327; - -import java.awt.EventQueue; - -import javax.swing.JLabel; - -import settings.ColorSetting; -import settings.Setting; -import alias.Alias; -import alias.AliasList; -import controller.channel.Channel; -import controller.channel.ProcessingChain; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; - -public class MPT1327TrafficPanel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mProtocolLabel; - private JLabel mFrequencyLabel; - private JLabel mChannelLabel; - - private JLabel mStateLabel; - private JLabel mFromTalkgroupLabel; - private JLabel mFromTalkgroupAliasLabel; - - private JLabel mToLabel; - private JLabel mToTalkgroupLabel; - private JLabel mToTalkgroupAliasLabel; - - private AliasList mAliasList; - - public MPT1327TrafficPanel( Channel channel ) - { - super( channel ); - - mAliasList = channel.getProcessingChain().getChannelState().getAliasList(); - - init(); - } - - public void dispose() - { - super.dispose(); - mAliasList = null; - } - - public void init() - { - mProtocolLabel = new JLabel( "MPT1327" ); - mProtocolLabel.setFont( mFontDecoder ); - mProtocolLabel.setForeground( mColorLabelDecoder ); - - mFrequencyLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mFrequencyLabel.setFont( mFontDetails ); - mFrequencyLabel.setForeground( mColorLabelDetails ); - - MPT1327ChannelState state = - (MPT1327ChannelState)mChannel.getProcessingChain().getChannelState(); - int channelNumber = state.getChannelNumber(); - - mChannelLabel = new JLabel( "Channel " + channelNumber ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mFromTalkgroupLabel = new JLabel( state.getFromTalkgroup() ); - mFromTalkgroupLabel.setFont( mFontDecoder ); - mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); - - mFromTalkgroupAliasLabel = new JLabel(); - mFromTalkgroupAliasLabel.setFont( mFontDecoder ); - mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( state.getFromTalkgroup() ); - - if( alias != null ) - { - mFromTalkgroupAliasLabel.setText( alias.getName() ); - - mFromTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( alias.getIconName(), 12 ) ); - } - } - - mToLabel = new JLabel( "TO:" ); - mToLabel.setFont( mFontDetails ); - mToLabel.setForeground( mColorLabelDetails ); - - mToTalkgroupLabel = new JLabel( state.getFromTalkgroup() ); - mToTalkgroupLabel.setFont( mFontDecoder ); - mToTalkgroupLabel.setForeground( mColorLabelDecoder ); - - mToTalkgroupAliasLabel = new JLabel(); - mToTalkgroupAliasLabel.setFont( mFontDecoder ); - mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( state.getFromTalkgroup() ); - - if( alias != null ) - { - mToTalkgroupAliasLabel.setText( alias.getName() ); - - mToTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( alias.getIconName(), 12 ) ); - } - } - - add( mProtocolLabel ); - add( mFrequencyLabel ); - add( mChannelLabel, "wrap" ); - - add( mStateLabel ); - add( mFromTalkgroupLabel ); - add( mFromTalkgroupAliasLabel, "wrap" ); - - add( mToLabel ); - add( mToTalkgroupLabel ); - add( mToTalkgroupAliasLabel, "wrap" ); - } - - @Override - public void receive( final ChangedAttribute changedAttribute ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - ProcessingChain chain = getChannel().getProcessingChain(); - - if( chain != null ) - { - final MPT1327ChannelState state = - (MPT1327ChannelState)chain.getChannelState(); - - if( state != null ) - { - switch( changedAttribute ) - { - case CHANNEL_STATE: - mStateLabel.setText( state.getState().getDisplayValue() ); - break; - case CHANNEL_NUMBER: - mChannelLabel.setText( "Channel " + state.getChannelNumber() ); - break; - case FROM_TALKGROUP: - String talkgroup = state.getFromTalkgroup(); - - mFromTalkgroupLabel.setText( talkgroup ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( talkgroup ); - - if( alias != null ) - { - mFromTalkgroupAliasLabel.setText( alias.getName() ); - - String icon = alias.getIconName(); - - mFromTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( icon, 12 ) ); - } - } - break; - case TO_TALKGROUP: - String toTalkgroup = state.getToTalkgroup(); - - mToTalkgroupLabel.setText( toTalkgroup ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( toTalkgroup ); - - if( alias != null ) - { - mToTalkgroupAliasLabel.setText( alias.getName() ); - - String icon = alias.getIconName(); - - if( icon != null ) - { - mToTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( icon, 12 ) ); - } - } - } - break; - default: - break; - } - - repaint(); - } - } - } - } ); - } - - @Override - public void settingChanged( Setting setting ) - { - super.settingChanged( setting ); - - if( setting instanceof ColorSetting ) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch( colorSetting.getColorSettingName() ) - { - case CHANNEL_STATE_LABEL_DECODER: - mProtocolLabel.setForeground( mColorLabelDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); - mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - mToTalkgroupLabel.setForeground( mColorLabelDecoder ); - mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - break; - case CHANNEL_STATE_LABEL_DETAILS: - mFrequencyLabel.setForeground( mColorLabelDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - mToLabel.setForeground( mColorLabelDetails ); - break; - } - } - } -} diff --git a/src/decode/nbfm/NBFMChannelState.java b/src/decode/nbfm/NBFMChannelState.java deleted file mode 100644 index fa4cd66a..00000000 --- a/src/decode/nbfm/NBFMChannelState.java +++ /dev/null @@ -1,69 +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 decode.nbfm; - -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; - -/** - * Conventional channel state - this class doesn't do anything at the moment, - * it is a place holder for future expansion for the conventional decoder - */ -public class NBFMChannelState extends ChannelState -{ - public NBFMChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - } - - /** - * Make the ConventionalChannelState always unsquelched - */ - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - - super.setSquelchState( SquelchState.UNSQUELCH ); - } - - @Override - public void setSquelchState( SquelchState state ) - { - //do nothing ... we want the squelch state always unsquelched (for now) - } - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "\tDecoder:\tNBFM\n\n" ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } -} diff --git a/src/decode/nbfm/NBFMDecoder.java b/src/decode/nbfm/NBFMDecoder.java deleted file mode 100644 index 5f8c1faa..00000000 --- a/src/decode/nbfm/NBFMDecoder.java +++ /dev/null @@ -1,99 +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 decode.nbfm; - -import sample.real.RealSampleListener; -import source.Source.SampleType; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class NBFMDecoder extends Decoder -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - private FilteringNBFMDemodulator mDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "NBFM Decoder Audio Output" ); - - public NBFMDecoder( DecodeConfiguration config, SampleType sampleType ) - { - super( config, sampleType ); - - /** - * Only setup a demod chain if we're receiving complex samples. If - * we're receiving demodulated samples, they'll be handled the same - * was as we handle the output of the demodulator. - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - /** - * The Decoder super class is both a Complex listener and a float - * listener. So, we add the demod to listen to the incoming - * quadrature samples, and we wire the output of the demod right - * back to this class, so we can receive the demodulated output - * to process - */ - mDemodulator = new FilteringNBFMDemodulator(); - this.addComplexListener( mDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mDemodulator.addListener( mDCRemovalFilter ); - - /** - * Route the demodulated, filtered samples back to this class to send - * to all registered listeners - */ - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - addRealSampleListener( mAudioOutput ); - } - - @Override - public DecoderType getType() - { - return DecoderType.NBFM; - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mDemodulator != null ) - { - mDemodulator.addListener( listener ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/nbfm/NBFMPanel.java b/src/decode/nbfm/NBFMPanel.java deleted file mode 100644 index f1555a49..00000000 --- a/src/decode/nbfm/NBFMPanel.java +++ /dev/null @@ -1,136 +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 decode.nbfm; - -import java.awt.EventQueue; - -import javax.swing.JLabel; - -import settings.ColorSetting; -import settings.Setting; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; - -public class NBFMPanel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - - private JLabel mProtocol = new JLabel( "NBFM" ); - - public NBFMPanel( Channel channel ) - { - super( channel ); - - init(); - } - - public void init() - { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - - mProtocol.setFont( mFontDecoder ); - mProtocol.setForeground( mColorLabelDecoder ); - - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - - add( mProtocol, "wrap" ); - } - - @Override - public void receive( final ChangedAttribute changedAttribute ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - switch( changedAttribute ) - { - case CHANNEL_STATE: - mStateLabel.setText( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - break; - case SOURCE: - mSourceLabel.setText( mChannel.getSourceConfiguration() - .getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; - } - - repaint(); - } - }); - } - - @Override - public void settingChanged( Setting setting ) - { - super.settingChanged( setting ); - - if( setting instanceof ColorSetting ) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch( colorSetting.getColorSettingName() ) - { - case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } - if( mProtocol != null ) - { - mProtocol.setForeground( mColorLabelDecoder ); - } - break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - break; - } - } - } -} diff --git a/src/decode/p25/P25ActivitySummary.java b/src/decode/p25/P25ActivitySummary.java deleted file mode 100644 index 65ac26fc..00000000 --- a/src/decode/p25/P25ActivitySummary.java +++ /dev/null @@ -1,350 +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 decode.p25; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import decode.p25.message.tsbk.osp.control.IdentifierUpdate; -import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; -import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import decode.p25.reference.Service; - -public class P25ActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - - private final static Logger mLog = - LoggerFactory.getLogger( P25ActivitySummary.class ); - - private static final DecimalFormat mFrequencyFormatter = - new DecimalFormat( "0.000000" ); - - private AliasList mAliasList; - - private NetworkStatusBroadcast mNetworkStatus; - private NetworkStatusBroadcastExtended mNetworkStatusExtended; - private ProtectionParameterBroadcast mProtectionParameterBroadcast; - private RFSSStatusBroadcast mRFSSStatusMessage; - private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; - private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; - private HashMap mNeighborMap = - new HashMap(); - - private Set mSecondaryControlChannels = - new TreeSet(); - - private HashMap mBands = - new HashMap(); - - public P25ActivitySummary( AliasList list ) - { - mAliasList = list; - } - - /** - * Cleanup method - */ - public void dispose() - { - } - - @Override - public void receive( Message message ) - { - if( !message.isValid() ) - { - mLog.debug( "Got an invalid message!" ); - } - - if( message instanceof TSBKMessage ) - { - if( message instanceof AdjacentStatusBroadcast ) - { - AdjacentStatusBroadcast neighborMessage = - (AdjacentStatusBroadcast)message; - - mNeighborMap.put( neighborMessage.getUniqueID(), neighborMessage ); - } - else if( message instanceof IdentifierUpdate ) - { - IdentifierUpdate iu = (IdentifierUpdate)message; - - if( !mBands.containsKey( iu.getIdentifier() ) ) - { - mBands.put( iu.getIdentifier(), iu ); - } - } - else if( message instanceof NetworkStatusBroadcast ) - { - mNetworkStatus = (NetworkStatusBroadcast)message; - } - else if( message instanceof RFSSStatusBroadcast ) - { - mRFSSStatusMessage = (RFSSStatusBroadcast)message; - } - else if( message instanceof SecondaryControlChannelBroadcast ) - { - SecondaryControlChannelBroadcast sccb = - (SecondaryControlChannelBroadcast)message; - - if( sccb.getDownlinkFrequency1() > 0 ) - { - mSecondaryControlChannels.add( - (SecondaryControlChannelBroadcast)message ); - } - } - else if( message instanceof SNDCPDataChannelAnnouncementExplicit ) - { - mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)message; - } - } - else if( message instanceof PDUMessage ) - { - if( message instanceof NetworkStatusBroadcastExtended ) - { - mNetworkStatusExtended = (NetworkStatusBroadcastExtended)message; - } - else if( message instanceof ProtectionParameterBroadcast ) - { - mProtectionParameterBroadcast = - (ProtectionParameterBroadcast)message; - } - else if( message instanceof RFSSStatusBroadcastExtended ) - { - mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)message; - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tP25\n" ); - sb.append( "===================== THIS SITE ======================" ); - - if( mNetworkStatus != null ) - { - sb.append( "\nNAC:\t" + mNetworkStatus.getNAC() ); - sb.append( "\nWACN-SYS:\t" + mNetworkStatus.getWACN() ); - sb.append( "-" + mNetworkStatus.getSystemID() ); - sb.append( " [" ); - sb.append( mNetworkStatus.getNetworkCallsign() ); - sb.append( "]" ); - sb.append( "\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea() ); - } - else if( mNetworkStatusExtended != null ) - { - sb.append( "\nNAC:\t" + mNetworkStatusExtended.getNAC() ); - sb.append( "\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN() ); - sb.append( "-" + mNetworkStatusExtended.getSystemID() ); - sb.append( " [" ); - sb.append( mNetworkStatusExtended.getNetworkCallsign() ); - sb.append( "]" ); - sb.append( "\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea() ); - } - - String site = null; - - if( mRFSSStatusMessage != null ) - { - site = mRFSSStatusMessage.getRFSubsystemID() + "-" + mRFSSStatusMessage.getSiteID(); - } - else if( mRFSSStatusMessageExtended != null ) - { - site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + - mRFSSStatusMessageExtended.getSiteID(); - } - - sb.append( "\nRFSS-SITE:\t" + site ); - - if( mAliasList != null ) - { - Alias siteAlias = mAliasList.getSiteID( site ); - - if( siteAlias != null ) - { - sb.append( " " + siteAlias.getName() ); - } - } - - if( mNetworkStatus != null ) - { - sb.append( "\nSERVICES:\t" + SystemService.toString( - mNetworkStatus.getSystemServiceClass() ) ); - sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)mNetworkStatus.getDownlinkFrequency() / 1E6d ) + - " [" + mNetworkStatus.getIdentifier() + "-" + - mNetworkStatus.getChannel() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)mNetworkStatus.getUplinkFrequency() / 1E6d ) + " [" + - mNetworkStatus.getIdentifier() + "-" + - mNetworkStatus.getChannel() + "]\n" ); - } - else if( mNetworkStatusExtended != null ) - { - sb.append( "\nSERVICES:\t" + SystemService.toString( - mNetworkStatusExtended.getSystemServiceClass() ) ); - sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d ) + - " [" + mNetworkStatusExtended.getTransmitIdentifier() + "-" + - mNetworkStatusExtended.getTransmitChannel() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d ) + - " [" + mNetworkStatusExtended.getReceiveIdentifier() + "-" + - mNetworkStatusExtended.getReceiveChannel() + "]" ); - } - - if( mSecondaryControlChannels.isEmpty() ) - { - sb.append( "\nSCCH:\tNONE" ); - } - else - { - for( SecondaryControlChannelBroadcast sec: mSecondaryControlChannels ) - { - sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)sec.getDownlinkFrequency1() / 1E6d ) + - " [" + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)sec.getUplinkFrequency1() / 1E6d ) + " [" + - sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); - - if( sec.hasChannel2() ) - { - sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)sec.getDownlinkFrequency2() / 1E6d ) + - " [" + sec.getIdentifier2() + "-" + sec.getChannel2() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)sec.getUplinkFrequency2() / 1E6d ) + " [" + - sec.getIdentifier2() + "-" + sec.getChannel2() + "]" ); - } - } - } - - if( mSNDCPDataChannel != null ) - { - sb.append( "\nSNDCP:" ); - sb.append( "\tDNLINK " + mFrequencyFormatter.format( - (double)mSNDCPDataChannel.getDownlinkFrequency()/1E6D ) + - " [" + mSNDCPDataChannel.getTransmitChannel() + "]" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)mSNDCPDataChannel.getUplinkFrequency()/1E6D ) + - " [" + mSNDCPDataChannel.getReceiveChannel() + "]" ); - } - - if( mProtectionParameterBroadcast != null ) - { - sb.append( "\nENCRYPTION:" ); - sb.append( "\nTYPE:\t" + mProtectionParameterBroadcast - .getEncryptionType().name() ); - sb.append( "\nALGORITHM:\t" + mProtectionParameterBroadcast - .getAlgorithmID() ); - sb.append( "\nKEY:\t" + mProtectionParameterBroadcast.getKeyID() ); - sb.append( "\nINBOUND IV:\t" + mProtectionParameterBroadcast - .getInboundInitializationVector() ); - sb.append( "\nOUTBOUND IV:\t" + mProtectionParameterBroadcast - .getOutboundInitializationVector() ); - } - - List identifiers = new ArrayList( mBands.keySet() ); - Collections.sort( identifiers ); - - sb.append( "\nFREQUENCY BANDS:" ); - for( Integer id: identifiers ) - { - IBandIdentifier band = mBands.get( id ); - - sb.append( "\n\t" + 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\n=================== NEIGHBORS ======================" ); - - if( mNeighborMap.isEmpty() ) - { - sb.append( "\n\tNONE\n" ); - sb.append( "\n----------------------------------------------------" ); - } - else - { - for( AdjacentStatusBroadcast neighbor: mNeighborMap.values() ) - { - sb.append( "\nNAC:\t" + neighbor.getNAC() ); - sb.append( "\nSYSTEM:\t" + neighbor.getSystemID() ); - sb.append( "\nLRA:\t" + neighbor.getLocationRegistrationArea() ); - - String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); - sb.append( "\nRFSS-SITE:\t" + neighborID ); - - if( mAliasList != null ) - { - Alias siteAlias = mAliasList.getSiteID( neighborID ); - - if( siteAlias != null ) - { - sb.append( " " + siteAlias.getName() ); - } - } - - sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)neighbor.getDownlinkFrequency() / 1E6d ) + - " [" + neighbor.getIdentifier() + "-" + neighbor.getChannel() + "]" ); - sb.append( "\n\tUPLINK:" + mFrequencyFormatter.format( - (double)neighbor.getUplinkFrequency() / 1E6d ) + "\n" ); - sb.append( "\nSERVICES:\t" + Service.getServices( neighbor.getSystemServiceClass() ) ); - sb.append( "\n----------------------------------------------------" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/p25/P25ChannelState.java b/src/decode/p25/P25ChannelState.java deleted file mode 100644 index 6a6831be..00000000 --- a/src/decode/p25/P25ChannelState.java +++ /dev/null @@ -1,3408 +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 decode.p25; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; -import alias.Alias; -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import controller.ThreadPoolManager; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import decode.p25.audio.P25AudioOutput; -import decode.p25.message.P25Message; -import decode.p25.message.hdu.HDUMessage; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.ldu.LDUMessage; -import decode.p25.message.ldu.lc.CallTermination; -import decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser; -import decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; -import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; -import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; -import decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; -import decode.p25.message.pdu.confirmed.SNDCPUserData; -import decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.CallAlertExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import decode.p25.message.pdu.osp.control.StatusQueryExtended; -import decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; -import decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcast; -import decode.p25.message.tdu.lc.GroupVoiceChannelUpdate; -import decode.p25.message.tdu.lc.GroupVoiceChannelUpdateExplicit; -import decode.p25.message.tdu.lc.NetworkStatusBroadcast; -import decode.p25.message.tdu.lc.NetworkStatusBroadcastExplicit; -import decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast; -import decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; -import decode.p25.message.tdu.lc.TDULinkControlMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import decode.p25.message.tsbk.osp.control.CallAlert; -import decode.p25.message.tsbk.osp.control.DenyResponse; -import decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; -import decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; -import decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import decode.p25.message.tsbk.osp.control.MessageUpdate; -import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import decode.p25.message.tsbk.osp.control.QueuedResponse; -import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import decode.p25.message.tsbk.osp.control.StatusQuery; -import decode.p25.message.tsbk.osp.control.StatusUpdate; -import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; -import decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import decode.p25.reference.IPProtocol; -import decode.p25.reference.Response; -import decode.p25.reference.Vendor; - -public class P25ChannelState extends ChannelState -{ - private final static Logger mLog = LoggerFactory.getLogger( P25ChannelState.class ); - - - private HashMap mRegistrations = new HashMap(); - private String mLastCommandEventID; - private String mLastPageEventID; - private String mLastQueryEventID; - private String mLastRegistrationEventID; - private String mLastResponseEventID; - - private ActiveCall mCurrentActiveCall; - private Map mCallDetects = new HashMap(); - - private P25ActivitySummary mActivitySummary; - private String mNAC; - private String mSystem; - private String mSite; - private String mSiteAlias; - private String mFromTalkgroup; - private Alias mFromAlias; - private String mToTalkgroup; - private Alias mToAlias; - - /* The currently tuned traffic channel - dynamically updated */ - private String mCurrentChannel = "CURRENT"; - private long mCurrentChannelFrequency = 0; - - private P25AudioOutput mAudioOutput; - - public P25ChannelState( ProcessingChain chain, AliasList aliasList ) - { - super( chain, aliasList ); - - mActivitySummary = new P25ActivitySummary( aliasList ); - - /* Get the channel frequency from the source config */ - SourceConfiguration source = chain.getChannel().getSourceConfiguration(); - - if( source instanceof SourceConfigTuner ) - { - mCurrentChannelFrequency = ((SourceConfigTuner)source).getFrequency(); - } - } - - public void setAudioOutput( P25AudioOutput audioOutput ) - { - mAudioOutput = audioOutput; - } - - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - - super.setSquelchState( SquelchState.UNSQUELCH ); - } - - /** - * Message processor. All messages are received and processed via this method. - */ - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof P25Message ) - { - updateNAC( ( (P25Message) message ).getNAC() ); - - mActivitySummary.receive( (P25Message)message ); - - if( message instanceof HDUMessage ) - { - /* Header Data Unit Message - preceeds voice LDUs */ - processHDU( (HDUMessage)message ); - } - if( message instanceof LDUMessage ) - { - /* Voice Vocoder messages */ - processLDU( (LDUMessage)message ); - } - else if( message instanceof PDUMessage ) - { - /* Packet Data Unit Messages */ - processPDU( (PDUMessage)message ); - } - else if( message instanceof TDULinkControlMessage ) - { - /* Terminator Data Unit with Link Control Message */ - processTDULC( (TDULinkControlMessage)message ); - } - else if( message instanceof TSBKMessage ) - { - /* Trunking Signalling Block Messages - indicates Control Channel */ - setState( State.CONTROL ); - - processTSBK( (TSBKMessage)message ); - } - } - } - - private void processHDU( HDUMessage hdu ) - { - State state = State.CALL; - - if( hdu.isValid() && hdu.isEncrypted() ) - { - state = State.ENCRYPTED; - } - - if( isActiveCall( mCurrentChannel, hdu.getToID() ) ) - { - updateCall( state, mCurrentChannel, null, hdu.getTalkgroupID() ); - } - else - { - StringBuilder sb = new StringBuilder(); - - CallEventType type = CallEventType.CALL; - - if( hdu.isValid() && hdu.isEncrypted() ) - { - type = CallEventType.ENCRYPTED_CALL; - - sb.append( "ENCRYPTED WITH " ); - sb.append( hdu.getEncryption().name() ); - sb.append( " KEY:" ); - sb.append( hdu.getKeyID() ); - } - - P25CallEvent event = new P25CallEvent.Builder( type ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .frequency( mCurrentChannelFrequency ) - .to( hdu.getToID() ) - .details( sb.toString() ) - .build(); - - addCall( event ); - } - } - - /** - * Processes Terminator Data Unit with Link Control messages that occur on - * the traffic channel - */ - private void processTDULC( TDULinkControlMessage tdulc ) - { - switch( tdulc.getOpcode() ) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if( tdulc instanceof AdjacentSiteStatusBroadcast ) - { - AdjacentSiteStatusBroadcast assb = - (AdjacentSiteStatusBroadcast)tdulc; - - updateSystem( assb.getSystemID() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - /* This message doesn't provide anything we need for channel state */ - break; - case CALL_ALERT: - if( tdulc instanceof decode.p25.message.tdu.lc.CallAlert ) - { - decode.p25.message.tdu.lc.CallAlert ca = - (decode.p25.message.tdu.lc.CallAlert)tdulc; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getSourceAddress() ) - .to( ca.getTargetAddress() ) - .details( "CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case CALL_TERMINATION_OR_CANCELLATION: - endAllCalls(); - 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 decode.p25.message.tdu.lc.ExtendedFunctionCommand ) - { - decode.p25.message.tdu.lc.ExtendedFunctionCommand efc = - (decode.p25.message.tdu.lc.ExtendedFunctionCommand)tdulc; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( efc.getTargetAddress() ) - .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_AFFILIATION_QUERY: - if( tdulc instanceof decode.p25.message.tdu.lc.GroupAffiliationQuery ) - { - decode.p25.message.tdu.lc.GroupAffiliationQuery gaq = - (decode.p25.message.tdu.lc.GroupAffiliationQuery)tdulc; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION QUERY" ) - .from( gaq.getSourceAddress() ) - .to( gaq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE: - /* Reflects other call activity on the system: CALL DETECT */ - if( tdulc instanceof GroupVoiceChannelUpdate ) - { - GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; - - if( isActiveCall( gvcu.getChannelA(), - gvcu.getGroupAddressA() ) ) - { - updateCall( State.CALL, - gvcu.getChannelA(), - gvcu.getGroupAddressA(), - gvcu.getGroupAddressB() ); - } - else - { - P25CallEvent gvcuEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcu.getChannelA() ) - .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcu.getDownlinkFrequencyA() ) - .from( gvcu.getGroupAddressA() ) - .to( gvcu.getGroupAddressB() ) - .build(); - - addCall( gvcuEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - /* Reflects other call activity on the system: CALL DETECT */ - - if( tdulc instanceof GroupVoiceChannelUpdateExplicit ) - { - GroupVoiceChannelUpdateExplicit gvcue = - (GroupVoiceChannelUpdateExplicit)tdulc; - if( isActiveCall( gvcue.getTransmitChannel(), - gvcue.getGroupAddress() ) ) - { - updateCall( State.CALL, - gvcue.getTransmitChannel(), - null, - gvcue.getGroupAddress() ); - } - else - { - P25CallEvent gvcueEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcue.getTransmitChannel() ) - .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcue.getDownlinkFrequency() ) - .to( gvcue.getGroupAddress() ) - .build(); - - addCall( gvcueEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_VOICE_CHANNEL_USER: - if( tdulc instanceof decode.p25.message.tdu.lc.GroupVoiceChannelUser ) - { - decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser = - (decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; - - if( isActiveCall( mCurrentChannel, gvcuser.getGroupAddress() ) ) - { - updateCall( State.CALL, - mCurrentChannel, - gvcuser.getSourceAddress(), - gvcuser.getGroupAddress() ); - } - else - { - P25CallEvent gvcuserEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( gvcuser.getSourceAddress() ) - .to( gvcuser.getGroupAddress() ) - .build(); - - addCall( gvcuserEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case MESSAGE_UPDATE: - if( tdulc instanceof decode.p25.message.tdu.lc.MessageUpdate ) - { - decode.p25.message.tdu.lc.MessageUpdate mu = - (decode.p25.message.tdu.lc.MessageUpdate)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .from( mu.getSourceAddress() ) - .to( mu.getTargetAddress() ) - .details( "MSG: " + mu.getShortDataMessage() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case NETWORK_STATUS_BROADCAST: - if( tdulc instanceof NetworkStatusBroadcast ) - { - updateSystem( ((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 decode.p25.message.tdu.lc.ProtectionParameterBroadcast ) - { - decode.p25.message.tdu.lc.ProtectionParameterBroadcast ppb = - (decode.p25.message.tdu.lc.ProtectionParameterBroadcast)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( ppb.getTargetAddress() ) - .details( "ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case RFSS_STATUS_BROADCAST: - if( tdulc instanceof decode.p25.message.tdu.lc.RFSSStatusBroadcast ) - { - decode.p25.message.tdu.lc.RFSSStatusBroadcast rfsssb = - (decode.p25.message.tdu.lc.RFSSStatusBroadcast)tdulc; - - updateSystem( rfsssb.getSystem() ); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - updateSite( 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 decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit ) - { - decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit)tdulc; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - updateSite( 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 SecondaryControlChannelBroadcast ) - { - SecondaryControlChannelBroadcast sccb = - (SecondaryControlChannelBroadcast)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( 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(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case STATUS_QUERY: - if( tdulc instanceof decode.p25.message.tdu.lc.StatusQuery ) - { - decode.p25.message.tdu.lc.StatusQuery sq = - (decode.p25.message.tdu.lc.StatusQuery)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceAddress() ) - .to( sq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case STATUS_UPDATE: - if( tdulc instanceof decode.p25.message.tdu.lc.StatusUpdate ) - { - decode.p25.message.tdu.lc.StatusUpdate su = - (decode.p25.message.tdu.lc.StatusUpdate)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.STATUS ) - .aliasList( mAliasList ) - .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 decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest ) - { - decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest tiar = - (decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( tiar.getTelephoneNumber() ) - .to( tiar.getTargetAddress() ) - .details( "TELEPHONE CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - if( tdulc instanceof decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser ) - { - decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser tivcu = - (decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc; - - if( isActiveCall( mCurrentChannel, tivcu.getAddress() ) ) - { - updateCall( State.CALL, - mCurrentChannel, - null, - tivcu.getAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "TELEPHONE INTERCONNECT " + - ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( null ) - .to( tivcu.getAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitAuthenticationCommand ) - { - decode.p25.message.tdu.lc.UnitAuthenticationCommand uac = - (decode.p25.message.tdu.lc.UnitAuthenticationCommand)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( uac.getCompleteTargetAddress() ) - .details( "AUTHENTICATE" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_REGISTRATION_COMMAND: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitRegistrationCommand ) - { - decode.p25.message.tdu.lc.UnitRegistrationCommand urc = - (decode.p25.message.tdu.lc.UnitRegistrationCommand)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( urc.getCompleteTargetAddress() ) - .details( "REGISTER" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitToUnitAnswerRequest ) - { - decode.p25.message.tdu.lc.UnitToUnitAnswerRequest uuar = - (decode.p25.message.tdu.lc.UnitToUnitAnswerRequest)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( uuar.getSourceAddress() ) - .to( uuar.getTargetAddress() ) - .details( "UNIT TO UNIT CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser ) - { - decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser uuvcu = - (decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; - - if( isActiveCall( mCurrentChannel, uuvcu.getTargetAddress() ) ) - { - updateCall( State.CALL, - mCurrentChannel, - uuvcu.getSourceAddress(), - uuvcu.getTargetAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) - .frequency( mCurrentChannelFrequency ) - .from( uuvcu.getSourceAddress() ) - .to( uuvcu.getTargetAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - default: - break; - } - } - - /** - * Log optional vendor format messages that we don't yet support so that we - * can understand those messages and eventually add support. - */ - private void logAlternateVendorMessage( P25Message p25 ) - { - if( p25 instanceof TDULinkControlMessage ) - { - TDULinkControlMessage tdulc = (TDULinkControlMessage)p25; - - mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() + - "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() + - " " + tdulc.getBinaryMessage() ); - } - else if( p25 instanceof LDU1Message ) - { - LDU1Message ldu1 = (LDU1Message)p25; - - mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() + - "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() + - " " + ldu1.getBinaryMessage() ); - } - else if( p25 instanceof PDUMessage ) - { - PDUMessage pdu = (PDUMessage)p25; - - mLog.debug( "PDU - VENDOR [" + pdu.getVendor() + - "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() + - " " + pdu.getBinaryMessage() ); - } - else - { - mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" ); - } - } - - private void processLDU( LDUMessage ldu ) - { - State state = getState(); - - /* Only change the state for valid ldus */ - if( ldu.isValid() ) - { - state = ldu.isEncrypted() ? State.ENCRYPTED : State.CALL; - } - - /* Send audio frames to the audio output */ - if( mAudioOutput != null && state != State.ENCRYPTED ) - { - mAudioOutput.receive( ldu ); - } - - if( ldu instanceof LDU1Message ) - { - switch( ((LDU1Message)ldu).getOpcode() ) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast ) - { - decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast assb = - (decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast)ldu; - - updateSystem( assb.getSystemID() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( State.CALL, mCurrentChannel, null, null ); - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - /* This message doesn't provide anything we need for channel state */ - updateCall( state, mCurrentChannel, null, null ); - break; - case CALL_ALERT: - if( ldu instanceof decode.p25.message.ldu.lc.CallAlert ) - { - decode.p25.message.ldu.lc.CallAlert ca = - (decode.p25.message.ldu.lc.CallAlert)ldu; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getSourceAddress() ) - .to( ca.getTargetAddress() ) - .details( "CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - updateCall( State.CALL, mCurrentChannel, null, null ); - break; - case CALL_TERMINATION_OR_CANCELLATION: - if( ldu instanceof CallTermination ) - { - endAllCalls(); - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case CHANNEL_IDENTIFIER_UPDATE: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - updateCall( state, mCurrentChannel, null, null ); - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - updateCall( state, mCurrentChannel, null, null ); - break; - case EXTENDED_FUNCTION_COMMAND: - if( ldu instanceof decode.p25.message.ldu.lc.ExtendedFunctionCommand ) - { - decode.p25.message.ldu.lc.ExtendedFunctionCommand efc = - (decode.p25.message.ldu.lc.ExtendedFunctionCommand)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( efc.getTargetAddress() ) - .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - updateCall( state, mCurrentChannel, null, null ); - break; - case GROUP_AFFILIATION_QUERY: - if( ldu instanceof decode.p25.message.ldu.lc.GroupAffiliationQuery ) - { - decode.p25.message.ldu.lc.GroupAffiliationQuery gaq = - (decode.p25.message.ldu.lc.GroupAffiliationQuery)ldu; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION QUERY" ) - .from( gaq.getSourceAddress() ) - .to( gaq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - updateCall( state, mCurrentChannel, null, null ); - break; - case GROUP_VOICE_CHANNEL_UPDATE: - if( ldu instanceof decode.p25.message.ldu.lc.GroupVoiceChannelUpdate ) - { - decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = - (decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; - - if( isActiveCall( gvcu.getChannelA(), - gvcu.getGroupAddressA() ) ) - { - updateCall( state, - gvcu.getChannelA(), - gvcu.getGroupAddressA(), - gvcu.getGroupAddressB() ); - } - else - { - P25CallEvent gvcuEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcu.getChannelA() ) - .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcu.getDownlinkFrequencyA() ) - .from( gvcu.getGroupAddressA() ) - .to( gvcu.getGroupAddressB() ) - .build(); - - addCall( gvcuEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit ) - { - decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = - (decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; - - if( isActiveCall( gvcue.getTransmitChannel(), - gvcue.getGroupAddress() ) ) - { - updateCall( state, - gvcue.getTransmitChannel(), - null, - gvcue.getGroupAddress() ); - } - else - { - P25CallEvent gvcueEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcue.getTransmitChannel() ) - .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcue.getDownlinkFrequency() ) - .to( gvcue.getGroupAddress() ) - .build(); - - addCall( gvcueEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case GROUP_VOICE_CHANNEL_USER: - if( ldu instanceof decode.p25.message.ldu.lc.GroupVoiceChannelUser ) - { - decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = - (decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; - - if( isActiveCall( mCurrentChannel, gvcuser.getGroupAddress() ) ) - { - updateCall( state, - mCurrentChannel, - gvcuser.getSourceAddress(), - gvcuser.getGroupAddress() ); - } - else - { - P25CallEvent gvcuserEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( gvcuser.getSourceAddress() ) - .to( gvcuser.getGroupAddress() ) - .build(); - - addCall( gvcuserEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case MESSAGE_UPDATE: - if( ldu instanceof decode.p25.message.ldu.lc.MessageUpdate ) - { - decode.p25.message.ldu.lc.MessageUpdate mu = - (decode.p25.message.ldu.lc.MessageUpdate)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .from( mu.getSourceAddress() ) - .to( mu.getTargetAddress() ) - .details( "MSG: " + mu.getShortDataMessage() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case NETWORK_STATUS_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.NetworkStatusBroadcast ) - { - updateSystem( ((decode.p25.message.ldu.lc.NetworkStatusBroadcast)ldu).getSystem() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit ) - { - updateSystem( ((decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit)ldu).getSystem() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case PROTECTION_PARAMETER_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.ProtectionParameterBroadcast ) - { - decode.p25.message.ldu.lc.ProtectionParameterBroadcast ppb = - (decode.p25.message.ldu.lc.ProtectionParameterBroadcast)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( ppb.getTargetAddress() ) - .details( "ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case RFSS_STATUS_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.RFSSStatusBroadcast ) - { - decode.p25.message.ldu.lc.RFSSStatusBroadcast rfsssb = - (decode.p25.message.ldu.lc.RFSSStatusBroadcast)ldu; - - updateSystem( rfsssb.getSystem() ); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit ) - { - decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit)ldu; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast ) - { - decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast sccb = - (decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit ) - { - decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit sccb = - (decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case STATUS_QUERY: - if( ldu instanceof decode.p25.message.ldu.lc.StatusQuery ) - { - decode.p25.message.ldu.lc.StatusQuery sq = - (decode.p25.message.ldu.lc.StatusQuery)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceAddress() ) - .to( sq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case STATUS_UPDATE: - if( ldu instanceof decode.p25.message.ldu.lc.StatusUpdate ) - { - decode.p25.message.ldu.lc.StatusUpdate su = - (decode.p25.message.ldu.lc.StatusUpdate)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.STATUS ) - .aliasList( mAliasList ) - .details( "STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus() ) - .from( su.getSourceAddress() ) - .to( su.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - updateCall( state, mCurrentChannel, null, null ); - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if( ldu instanceof decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest ) - { - decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest tiar = - (decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( tiar.getTelephoneNumber() ) - .to( tiar.getTargetAddress() ) - .details( "TELEPHONE CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - if( ldu instanceof TelephoneInterconnectVoiceChannelUser ) - { - TelephoneInterconnectVoiceChannelUser tivcu = - (TelephoneInterconnectVoiceChannelUser)ldu; - - if( isActiveCall( mCurrentChannel, tivcu.getAddress() ) ) - { - updateCall( state, - mCurrentChannel, - null, - tivcu.getAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "TELEPHONE INTERCONNECT " + - ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( null ) - .to( tivcu.getAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if( ldu instanceof decode.p25.message.ldu.lc.UnitAuthenticationCommand ) - { - decode.p25.message.ldu.lc.UnitAuthenticationCommand uac = - (decode.p25.message.ldu.lc.UnitAuthenticationCommand)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( uac.getCompleteTargetAddress() ) - .details( "AUTHENTICATE" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case UNIT_REGISTRATION_COMMAND: - if( ldu instanceof decode.p25.message.ldu.lc.UnitRegistrationCommand ) - { - decode.p25.message.ldu.lc.UnitRegistrationCommand urc = - (decode.p25.message.ldu.lc.UnitRegistrationCommand)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( urc.getCompleteTargetAddress() ) - .details( "REGISTER" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if( ldu instanceof decode.p25.message.ldu.lc.UnitToUnitAnswerRequest ) - { - decode.p25.message.ldu.lc.UnitToUnitAnswerRequest uuar = - (decode.p25.message.ldu.lc.UnitToUnitAnswerRequest)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( uuar.getSourceAddress() ) - .to( uuar.getTargetAddress() ) - .details( "UNIT TO UNIT CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - if( ldu instanceof UnitToUnitVoiceChannelUser ) - { - UnitToUnitVoiceChannelUser uuvcu = - (UnitToUnitVoiceChannelUser)ldu; - - if( isActiveCall( mCurrentChannel, uuvcu.getTargetAddress() ) ) - { - updateCall( state, - mCurrentChannel, - uuvcu.getSourceAddress(), - uuvcu.getTargetAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) - .frequency( mCurrentChannelFrequency ) - .from( uuvcu.getSourceAddress() ) - .to( uuvcu.getTargetAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - default: - break; - } - } - else - { - //LDU2 - updateCall( state, mCurrentChannel, null, null ); - } - } - - /** - * Process a Trunking Signalling Block message - */ - private void processTSBK( TSBKMessage tsbk ) - { - if( tsbk.getVendor() == Vendor.STANDARD ) - { - switch( tsbk.getOpcode() ) - { - case ACKNOWLEDGE_RESPONSE: - processTSBKResponse( tsbk ); - break; - case AUTHENTICATION_COMMAND: - processTSBKCommand( tsbk ); - break; - case CALL_ALERT: - processTSBKCall( 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: - 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: - processTSBKCall( tsbk ); - break; - case LOCATION_REGISTRATION_RESPONSE: - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - processTSBKResponse( tsbk ); - break; - case MESSAGE_UPDATE: - processTSBKMessage( 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 SNDCP_DATA_CHANNEL_GRANT: - processTSBKCall( 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; - } - } - } - - /** - * Process a Packet Data Unit message - */ - private void processPDU( PDUMessage pdu ) - { - if( pdu instanceof PDUConfirmedMessage ) - { - PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; - - switch( pduc.getPDUType() ) - { - case SN_ACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPActivateTDSContextAccept satca = - (SNDCPActivateTDSContextAccept)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "ACTIVATE SNDCP USE IP:" + satca.getIPAddress() ) - .frequency( mCurrentChannelFrequency ) - .to( satca.getLogicalLinkID() ) - .build() ); - break; - case SN_ACTIVATE_TDS_CONTEXT_REJECT: - SNDCPActivateTDSContextReject satcr = - (SNDCPActivateTDSContextReject)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "REJECT: SNDCP CONTEXT ACTIVATION " - + "REASON:" + satcr.getReason().getLabel() ) - .frequency( mCurrentChannelFrequency ) - .to( satcr.getLogicalLinkID() ) - .build() ); - break; - case SN_ACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPActivateTDSContextRequest satcreq = - (SNDCPActivateTDSContextRequest)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "REQUEST SNDCP USE IP:" + satcreq.getIPAddress() ) - .frequency( mCurrentChannelFrequency ) - .from( satcreq.getLogicalLinkID() ) - .build() ); - break; - case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPDeactivateTDSContext sdtca = - (SNDCPDeactivateTDSContext)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "ACCEPT DEACTIVATE SNDCP CONTEXT" ) - .frequency( mCurrentChannelFrequency ) - .from( sdtca.getLogicalLinkID() ) - .build() ); - break; - case SN_DEACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPDeactivateTDSContext sdtcreq = - (SNDCPDeactivateTDSContext)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "REQUEST DEACTIVATE SNDCP CONTEXT" ) - .frequency( mCurrentChannelFrequency ) - .from( sdtcreq.getLogicalLinkID() ) - .build() ); - break; - case SN_RF_CONFIRMED_DATA: - 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() ); - } - - P25CallEvent dataEvent = new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "DATA: " + sud.getPayload() + - " RADIO IP:" + sbTo.toString() ) - .frequency( mCurrentChannelFrequency ) - .from( sbFrom.toString() ) - .to( pduc.getLogicalLinkID() ) - .build(); - - if( isActiveCall( mCurrentChannel, pduc.getLogicalLinkID() ) ) - { - updateCall( State.DATA, mCurrentChannel, sbFrom.toString(), pduc.getLogicalLinkID() ); - } - else - { - addCall( dataEvent ); - } - break; - default: - mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); - break; - } - } - else - { - switch( pdu.getOpcode() ) - { - case ADJACENT_STATUS_BROADCAST: - AdjacentStatusBroadcastExtended asbe = - (AdjacentStatusBroadcastExtended)pdu; - - updateSystem( asbe.getSystemID() ); - break; - case CALL_ALERT: - if( pdu instanceof CallAlertExtended ) - { - CallAlertExtended ca = (CallAlertExtended)pdu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getWACN() + "-" + ca.getSystemID() + "-" + - ca.getSourceID() ) - .to( ca.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case GROUP_AFFILIATION_QUERY: - if( pdu instanceof GroupAffiliationQueryExtended ) - { - GroupAffiliationQueryExtended gaqe = - (GroupAffiliationQueryExtended)pdu; - - if( mLastQueryEventID == null || !gaqe.getTargetAddress() - .contentEquals( mLastQueryEventID ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION" ) - .from( gaqe.getWACN() + "-" + gaqe.getSystemID() + - "-" + gaqe.getSourceID() ) - .to( gaqe.getTargetAddress() ) - .build() ); - - mLastQueryEventID = gaqe.getToID(); - } - } - break; - case GROUP_AFFILIATION_RESPONSE: - if( pdu instanceof GroupAffiliationResponseExtended ) - { - GroupAffiliationResponseExtended gar = - (GroupAffiliationResponseExtended)pdu; - - if( mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .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 ); - } - break; - case GROUP_DATA_CHANNEL_GRANT: - if( pdu instanceof GroupDataChannelGrantExtended ) - { - GroupDataChannelGrantExtended gdcge = - (GroupDataChannelGrantExtended)pdu; - - if( isActiveCall( gdcge.getTransmitChannel(), gdcge.getGroupAddress() ) ) - { - updateCall( State.DATA, - gdcge.getTransmitChannel(), - gdcge.getSourceAddress(), - gdcge.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdcge.getTransmitChannel() ) - .details( ( gdcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdcge.getDownlinkFrequency() ) - .from( gdcge.getSourceAddress() ) - .to( gdcge.getGroupAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - if( pdu instanceof GroupVoiceChannelGrantExplicit ) - { - GroupVoiceChannelGrantExplicit gvcge = - (GroupVoiceChannelGrantExplicit)pdu; - - if( isActiveCall( gvcge.getTransmitChannel(), gvcge.getGroupAddress() ) ) - { - updateCall( State.CALL, - gvcge.getTransmitChannel(), - gvcge.getSourceAddress(), - gvcge.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcge.getTransmitChannel() ) - .details( ( gvcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcge.getDownlinkFrequency() ) - .from( gvcge.getSourceAddress() ) - .to( gvcge.getGroupAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - if( pdu instanceof IndividualDataChannelGrantExtended ) - { - IndividualDataChannelGrantExtended idcge = - (IndividualDataChannelGrantExtended)pdu; - - if( isActiveCall( idcge.getTransmitChannel(), - idcge.getTargetAddress() ) ) - { - updateCall( State.DATA, - idcge.getTransmitChannel(), - idcge.getSourceAddress(), - idcge.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( idcge.getTransmitChannel() ) - .details( ( idcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( idcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( idcge.getDownlinkFrequency() ) - .from( idcge.getSourceWACN() + "-" + - idcge.getSourceSystemID() + "-" + - idcge.getSourceAddress() ) - .to( idcge.getTargetAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case MESSAGE_UPDATE: - if( pdu instanceof MessageUpdateExtended ) - { - MessageUpdateExtended mu = (MessageUpdateExtended)pdu; - - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .details( "MESSAGE: " + mu.getMessage() ) - .from( mu.getSourceWACN() + "-" + mu.getSourceSystemID() + - "-" + mu.getSourceID() ) - .to( mu.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case RFSS_STATUS_BROADCAST: - if( pdu instanceof RFSSStatusBroadcastExtended ) - { - RFSSStatusBroadcastExtended rsbe = (RFSSStatusBroadcastExtended)pdu; - - updateNAC( rsbe.getNAC() ); - updateSystem( rsbe.getSystemID() ); - updateSite( rsbe.getRFSubsystemID() + "-" + rsbe.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() ); - } - - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .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 ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .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() ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .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 TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectChannelGrantExplicit ticge = - (TelephoneInterconnectChannelGrantExplicit)pdu; - - if( isActiveCall( ticge.getTransmitChannel(), ticge.getAddress() )) - { - updateCall( State.CALL, - ticge.getTransmitChannel(), - ticge.getLogicalLinkID(), - ticge.getAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( ticge.getTransmitChannel() ) - .details( "TELEPHONE CALL " + - ( ticge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( ticge.isEmergency() ? " EMERGENCY" : "") + - " CALL TIMER:" + ticge.getCallTimer() ) - .frequency( ticge.getDownlinkFrequency() ) - .from( ticge.getAddress() ) - .build(); - - addCall( event ); - } - 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() ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .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() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( utuare.isEmergency() ? "EMERGENCY" : "" ) ) - .from( utuare.getWACN() + "-" + - utuare.getSystemID() + "-" + - utuare.getSourceID() ) - .to( utuare.getTargetAddress() ) - .build() ); - - mLastPageEventID = utuare.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - if( pdu instanceof UnitToUnitVoiceChannelGrantExtended ) - { - UnitToUnitVoiceChannelGrantExtended uuvcge = - (UnitToUnitVoiceChannelGrantExtended)pdu; - - if( isActiveCall( uuvcge.getTransmitChannel(), uuvcge.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcge.getTransmitChannel(), - uuvcge.getSourceAddress(), - uuvcge.getTargetAddress() ); - } - else - { - P25CallEvent uuvcgeEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcge.getTransmitChannel() ) - .details( ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcge.getDownlinkFrequency() ) - .from( uuvcge.getSourceWACN() + "-" + - uuvcge.getSourceSystemID() + "-" + - uuvcge.getSourceID() ) - .to( uuvcge.getTargetAddress() ) - .build(); - - addCall( uuvcgeEvent ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - if( pdu instanceof UnitToUnitVoiceChannelGrantUpdateExtended ) - { - UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = - (UnitToUnitVoiceChannelGrantUpdateExtended)pdu; - - if( isActiveCall( uuvcgue.getTransmitChannel(), - uuvcgue.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcgue.getTransmitChannel(), - uuvcgue.getSourceAddress(), - uuvcgue.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcgue.getTransmitChannel() ) - .details( ( uuvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcgue.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcgue.getDownlinkFrequency() ) - .from( uuvcgue.getSourceWACN() + "-" + - uuvcgue.getSourceSystemID() + "-" + - uuvcgue.getSourceID() ) - .to( uuvcgue.getTargetAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - default: - break; - } - } - } - - private void processTSBKCommand( TSBKMessage message ) - { - P25CallEvent event = null; - - switch( message.getOpcode() ) - { - case AUTHENTICATION_COMMAND: - AuthenticationCommand ac = (AuthenticationCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( ac.getFullTargetID() )) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( "AUTHENTICATE" ) - .to( ac.getWACN() + "-" + ac.getSystemID() + "-" + - ac.getTargetID() ) - .build(); - - mLastCommandEventID = ac.getFullTargetID(); - } - break; - case EXTENDED_FUNCTION_COMMAND: - ExtendedFunctionCommand efc = (ExtendedFunctionCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( efc.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.FUNCTION ) - .aliasList( mAliasList ) - .details( "EXTENDED FUNCTION: " + - efc.getExtendedFunction().getLabel() ) - .from( efc.getSourceAddress() ) - .to( efc.getTargetAddress() ) - .build(); - - mLastCommandEventID = efc.getTargetAddress(); - } - break; - case RADIO_UNIT_MONITOR_COMMAND: - RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( rumc.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .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() ) ) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( rac.getStackOperation().name() + - " ROAMING ADDRESS " + rac.getWACN() + "-" + - rac.getSystemID() ) - .to( rac.getTargetID() ) - .build(); - - mLastCommandEventID = rac.getTargetID(); - } - break; - case UNIT_REGISTRATION_COMMAND: - UnitRegistrationCommand urc = (UnitRegistrationCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( urc.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( "REGISTER" ) - .from( urc.getSourceAddress() ) - .to( urc.getTargetAddress() ) - .build(); - - mLastCommandEventID = urc.getTargetAddress(); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - } - } - - private void processTSBKMessage( TSBKMessage message ) - { - MessageUpdate mu = (MessageUpdate)message; - - CallEvent event = - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .details( "MESSAGE: " + mu.getMessage() ) - .from( mu.getSourceAddress() ) - .to( mu.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - } - - private void processTSBKQuery( TSBKMessage message ) - { - CallEvent event = null; - - switch( message.getOpcode() ) - { - case GROUP_AFFILIATION_QUERY: - GroupAffiliationQuery gaq = (GroupAffiliationQuery)message; - - if( mLastQueryEventID == null || !mLastQueryEventID - .contentEquals( gaq.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION" ) - .from( gaq.getSourceAddress() ) - .to( gaq.getTargetAddress() ) - .build(); - } - break; - case STATUS_QUERY: - StatusQuery sq = (StatusQuery)message; - - if( mLastQueryEventID == null || !mLastQueryEventID - .contentEquals( sq.getTargetAddress() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceAddress() ) - .to( sq.getTargetAddress() ) - .build() ); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - - mLastQueryEventID = event.getToID(); - } - } - - private void processTSBKResponse( TSBKMessage message ) - { - CallEvent event = null; - - switch( message.getOpcode() ) - { - case ACKNOWLEDGE_RESPONSE: - AcknowledgeResponse ar = (AcknowledgeResponse)message; - - if( mLastResponseEventID == null || !ar.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "ACKNOWLEDGE" ) - .to( ar.getTargetAddress() ) - .build(); - - if( ar.hasAdditionalInformation() ) - { - if( ar.hasExtendedAddress() ) - { - event.setToID( ar.getWACN() + "-" + ar.getSystemID() + "-" + - ar.getTargetAddress() ); - } - else - { - event.setFromID( ar.getFromID() ); - } - } - - mLastResponseEventID = ar.getTargetAddress(); - } - break; - case DENY_RESPONSE: - DenyResponse dr = (DenyResponse)message; - - if( mLastResponseEventID == null || !dr.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .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 ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .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() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .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 ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .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 ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "QUEUED REASON: " + qr.getReason().name() + - " REQUESTED: " + qr.getServiceType().name() ) - .from( qr.getSourceAddress() ) - .to( qr.getTargetAddress() ) - .build(); - - mLastResponseEventID = qr.getTargetAddress(); - } - break; - case STATUS_UPDATE: - StatusUpdate su = (StatusUpdate)message; - - if( mLastResponseEventID == null || !su.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus() ) - .from( su.getSourceAddress() ) - .to( su.getTargetAddress() ) - .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() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .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() ) ) - { - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.DEREGISTER ) - .aliasList( mAliasList ) - .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; - } - - if( event != null ) - { - mCallEventModel.add( event ); - } - } - - private void processTSBKRFSSStatus( RFSSStatusBroadcast message ) - { - updateNAC( message.getNAC() ); - - updateSystem( message.getSystemID() ); - - updateSite( message.getRFSubsystemID() + "-" + message.getSiteID() ); - } - - /** - * Process a call event message - */ - private void processTSBKCall( TSBKMessage message ) - { - switch( message.getOpcode() ) - { - case CALL_ALERT: - CallAlert ca = (CallAlert)message; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getSourceID() ) - .to( ca.getTargetAddress() ) - .build() ); - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - GroupDataChannelAnnouncement gdca = (GroupDataChannelAnnouncement)message; - - if( isActiveCall( gdca.getChannel1(), gdca.getGroupAddress1() ) ) - { - updateCall( State.DATA, - gdca.getChannel1(), - gdca.getGroupAddress2(), - gdca.getGroupAddress1() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdca.getChannel1() ) - .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdca.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdca.getDownlinkFrequency1() ) - .to( gdca.getGroupAddress1() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - - if( gdca.hasChannelNumber2() ) - { - if( isActiveCall( gdca.getChannel2(), gdca.getGroupAddress2() ) ) - { - updateCall( State.DATA, - gdca.getChannel2(), - null, - gdca.getGroupAddress2() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdca.getChannel2() ) - .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdca.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdca.getDownlinkFrequency2() ) - .to( gdca.getGroupAddress2() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - } - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - GroupDataChannelAnnouncementExplicit gdcae = - (GroupDataChannelAnnouncementExplicit)message; - - if( isActiveCall( gdcae.getTransmitChannel(), gdcae.getGroupAddress() ) ) - { - updateCall( State.DATA, - gdcae.getTransmitChannel(), - null, - gdcae.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdcae.getTransmitChannel() ) - .details( ( gdcae.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdcae.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdcae.getDownlinkFrequency() ) - .to( gdcae.getGroupAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case GROUP_DATA_CHANNEL_GRANT: - GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; - - if( isActiveCall( gdcg.getChannel(), gdcg.getGroupAddress() ) ) - { - updateCall( State.DATA, - gdcg.getChannel(), null, - gdcg.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdcg.getChannel() ) - .details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdcg.getDownlinkFrequency() ) - .from( gdcg.getSourceAddress() ) - .to( gdcg.getGroupAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; - - if( isActiveCall( gvcg.getChannel(), gvcg.getGroupAddress() ) ) - { - updateCall( State.CALL, - gvcg.getChannel(), - gvcg.getSourceAddress(), - gvcg.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcg.getChannel() ) - .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcg.getDownlinkFrequency() ) - .from( gvcg.getSourceAddress() ) - .to( gvcg.getGroupAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - GroupVoiceChannelGrantUpdate gvcgu = - (GroupVoiceChannelGrantUpdate)message; - - if( isActiveCall( gvcgu.getChannel1(), gvcgu.getGroupAddress1() ) ) - { - updateCall( State.CALL, - gvcgu.getChannel1(), - gvcgu.getGroupAddress2(), - gvcgu.getGroupAddress1() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannel1() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency1() ) - .to( gvcgu.getGroupAddress1() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - - if( gvcgu.hasChannelNumber2() ) - { - if( isActiveCall( gvcgu.getChannel2(), gvcgu.getGroupAddress2() ) ) - { - updateCall( State.CALL, - gvcgu.getChannel2(), null, - gvcgu.getGroupAddress2() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannel2() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency2() ) - .to( gvcgu.getGroupAddress2() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - GroupVoiceChannelGrantUpdateExplicit gvcgue = - (GroupVoiceChannelGrantUpdateExplicit)message; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgue.getTransmitChannelIdentifier() + "-" + - gvcgue.getTransmitChannelNumber() ) - .details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgue.getDownlinkFrequency() ) - .to( gvcgue.getGroupAddress() ) - .build() ); - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; - - if( isActiveCall( idcg.getChannel(), idcg.getTargetAddress() ) ) - { - updateCall( State.DATA, - idcg.getChannel(), - idcg.getSourceAddress(), - idcg.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( idcg.getChannel() ) - .details( ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( idcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( idcg.getDownlinkFrequency() ) - .from( idcg.getSourceAddress() ) - .to( idcg.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case SNDCP_DATA_CHANNEL_GRANT: - SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; - - if( isActiveCall( sdcg.getTransmitChannel(), sdcg.getTargetAddress() ) ) - { - updateCall( State.DATA, - sdcg.getTransmitChannel(), - null, - sdcg.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( sdcg.getTransmitChannel() ) - .details( "SNDCP DATA NSAPI:" + sdcg.getNSAPI() ) - .frequency( sdcg.getDownlinkFrequency() ) - .to( sdcg.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectVoiceChannelGrant tivcg = - (TelephoneInterconnectVoiceChannelGrant)message; - - if( isActiveCall( tivcg.getChannel(), tivcg.getAddress() )) - { - updateCall( State.CALL, - tivcg.getChannel(), - tivcg.getSourceAddress(), - tivcg.getAddress() ); - } - else - { - P25CallEvent tivcgEvent = - new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) - .aliasList( mAliasList ) - .channel( tivcg.getChannel() ) - .details( ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcg.isEmergency() ? " EMERGENCY" : "") + - " CALL TIMER:" + tivcg.getCallTimer() ) - .frequency( tivcg.getDownlinkFrequency() ) - .from( tivcg.getAddress() ) - .build(); - - mCallEventModel.add( tivcgEvent ); - - addCall( tivcgEvent ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = - (TelephoneInterconnectVoiceChannelGrantUpdate)message; - - if( isActiveCall( tivcgu.getChannel(), tivcgu.getAddress() ) ) - { - updateCall( State.CALL, - tivcgu.getChannel(), - tivcgu.getSourceAddress(), - tivcgu.getAddress() ); - } - else - { - P25CallEvent tivcguEvent = - new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) - .aliasList( mAliasList ) - .channel( tivcgu.getChannelIdentifier() + "-" + - tivcgu.getChannelNumber() ) - .details( ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( tivcgu.getDownlinkFrequency() ) - .from( tivcgu.getAddress() ) - .build(); - - mCallEventModel.add( tivcguEvent ); - - addCall( tivcguEvent ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - UnitToUnitVoiceChannelGrant uuvcg = - (UnitToUnitVoiceChannelGrant)message; - - if( isActiveCall( uuvcg.getChannel(), uuvcg.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcg.getChannel(), - uuvcg.getSourceAddress(), - uuvcg.getTargetAddress() ); - } - else - { - P25CallEvent uuvcgEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcg.getChannelIdentifier() + "-" + - uuvcg.getChannelNumber() ) - .details( ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcg.getDownlinkFrequency() ) - .from( uuvcg.getSourceAddress() ) - .to( uuvcg.getTargetAddress() ) - .build(); - - mCallEventModel.add( uuvcgEvent ); - - addCall( uuvcgEvent ); - } - - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - UnitToUnitVoiceChannelGrantUpdate uuvcgu = - (UnitToUnitVoiceChannelGrantUpdate)message; - - if( isActiveCall( uuvcgu.getChannel(), uuvcgu.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcgu.getChannel(), - uuvcgu.getSourceAddress(), - uuvcgu.getTargetAddress() ); - } - else - { - P25CallEvent uuvcguEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcgu.getChannelIdentifier() + "-" + - uuvcgu.getChannelNumber() ) - .details( ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcgu.getDownlinkFrequency() ) - .from( uuvcgu.getSourceAddress() ) - .to( uuvcgu.getTargetAddress() ) - .build(); - - mCallEventModel.add( uuvcguEvent ); - - addCall( uuvcguEvent ); - } - - break; - default: - break; - } - } - - /** - * Indicates if either the current call, or any current call detects contain - * the talkgroup id. For the current call, only the TO talkgroup is tested - * - * @param channel - * @param talkgroup - * @return - */ - private boolean isActiveCall( String channel, String talkgroup ) - { - if( channel != null ) - { - if( mCurrentChannel.contentEquals( channel ) ) - { - return mCurrentActiveCall != null && - ( talkgroup == null || - mCurrentActiveCall.getCallEvent().getToID().contentEquals( talkgroup ) ); - } - else - { - ActiveCall call = mCallDetects.get( channel ); - - if( call != null ) - { - return talkgroup != null && call.getTalkgroups().contains( talkgroup ); - } - } - } - - return false; - } - - /** - * Starts a new call event. For P25 Phase 1, only 1 call event should ever - * have a call event type of CALL. All other detected call events on other - * channels should use call event type CALL DETECT. - */ - private void addCall( P25CallEvent event ) - { - mCallEventModel.add( event ); - - String channel = event.getChannel(); - - ActiveCall call = new ActiveCall( getProcessingChain() - .getResourceManager().getThreadPoolManager(), event ); - - if( channel.contentEquals( mCurrentChannel ) ) - { - if( mCurrentActiveCall != null ) - { - mCurrentActiveCall.end(); - } - - mCurrentActiveCall = call; - - switch( event.getCallEventType() ) - { - case CALL: - setState( State.CALL ); - updateFrom( event.getFromID() ); - updateTo( event.getToID() ); - break; - case DATA_CALL: - setState( State.DATA ); - updateFrom( event.getFromID() ); - updateTo( event.getToID() ); - break; - case CALL_DETECT: - /* don't update call state */ - default: - break; - } - } - else - { - /* End the current channel call event, if there is one */ - if( mCallDetects.containsKey( channel ) ) - { - mCallDetects.get( channel ).end(); - } - - mCallDetects.put( channel, call ); - } - - } - - /** - * Ends all calls in the mActiveCalls queue - */ - private void endAllCalls() - { - if( mCurrentActiveCall != null ) - { - mCurrentActiveCall.end(); - } - - for( ActiveCall call: mCallDetects.values() ) - { - call.end(); - } - } - - /** - * Updates state, channel, from and to for a currently active call. Use the - * isActiveCall() method to verify that a call is currently active, otherwise - * this invocation is ignored - */ - private void updateCall( State state, - String channel, - String from, - String to ) - { - if( mCurrentChannel.contentEquals( channel ) ) - { - if( mCurrentActiveCall != null ) - { - mCurrentActiveCall.update(); - - P25CallEvent event = mCurrentActiveCall.getCallEvent(); - - switch( event.getCallEventType() ) - { - case CALL: - setState( State.CALL ); - - /* Broacast call state update */ - updateFrom( from ); - updateTo( to ); - - if( from != null && - !from.contentEquals( "0000" ) && - !from.contentEquals( "000000" ) && - ( !event.hasFromID() || !event.getFromID().contentEquals( from ) ) ) - { - event.setFromID( from ); - } - break; - case DATA_CALL: - setState( State.DATA ); - - updateFrom( from ); - updateTo( to ); - - if( from != null && - !from.contentEquals( "0000" ) && - !from.contentEquals( "000000" ) && - ( !event.hasFromID() || !event.getFromID().contentEquals( from ) ) ) - { - event.setFromID( from ); - } - break; - case CALL_DETECT: - /* don't update call state */ - default: - break; - } - } - else - { - mLog.debug( "Request to update non-existent current call with " - + "state:" + state + " channel:" + channel + " from:" + - from + " to:" + to ); - } - } - else - { - ActiveCall call = mCallDetects.get( channel ); - - if( call != null ) - { - /* Update the timestamp to now */ - call.update(); - } - else - { - mLog.debug( "Request to update non-existent call detect with " - + "state:" + state + " channel:" + channel + " from:" + - from + " to:" + to ); - } - } - } - - /** - * Broadcasts an update for the TO group or unit address - */ - private void updateTo( String to ) - { - if( to != null && - !to.contentEquals( "0000" ) && - !to.contentEquals( "000000" ) && - ( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) ) - { - mToTalkgroup = to; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( mAliasList != null ) - { - mToAlias = mAliasList.getTalkgroupAlias( to ); - } - else - { - mToAlias = null; - } - - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - - /** - * Broadcasts an update for the FROM group or unit address - */ - private void updateFrom( String from ) - { - if( from != null && - !from.contentEquals( "0000" ) && - !from.contentEquals( "000000" ) && - ( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) ) - { - mFromTalkgroup = from; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - if( mAliasList != null ) - { - mFromAlias = mAliasList.getTalkgroupAlias( from ); - } - else - { - mFromAlias = null; - } - - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } - } - - /** - * Broadcasts an update to the NAC - */ - private void updateNAC( String nac ) - { - if( mNAC == null && nac != null ) - { - mNAC = nac; - - broadcastChange( ChangedAttribute.NAC ); - } - } - - /** - * Updates the site information - */ - private void updateSite( String site ) - { - if( mSite == null || !mSite.contentEquals( site ) ) - { - mSite = site; - - broadcastChange( ChangedAttribute.SITE ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getSiteID( mSite ); - - if( alias != null ) - { - mSiteAlias = alias.getName(); - } - else - { - mSiteAlias = null; - } - } - - broadcastChange( ChangedAttribute.SITE_ALIAS ); - } - } - - private void updateSystem( String system ) - { - if( mSystem == null || !mSystem.contentEquals( system ) ) - { - mSystem = system; - - broadcastChange( ChangedAttribute.SYSTEM ); - } - } - - - /** - * Process a unit paging event message - */ - private void processTSBKPage( TSBKMessage message ) - { - P25CallEvent event = null; - - switch( message.getOpcode() ) - { - case UNIT_TO_UNIT_ANSWER_REQUEST: - UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; - - if( mLastPageEventID == null || !mLastPageEventID - .contentEquals( utuar.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .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() ) ) - { - event = new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( "SNDCP DATA DAC: " + - sdpr.getDataAccessControl() + - " NSAPI:" + sdpr.getNSAPI() ) - .to( sdpr.getTargetAddress() ) - .build(); - - mLastPageEventID = sdpr.getTargetAddress(); - } - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - TelephoneInterconnectAnswerRequest tiar = - (TelephoneInterconnectAnswerRequest)message; - - if( mLastPageEventID == null || !mLastPageEventID - .contentEquals( tiar.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( "TELEPHONE INTERCONNECT" ) ) - .from( tiar.getTelephoneNumber() ) - .to( tiar.getTargetAddress() ) - .build(); - - mLastPageEventID = tiar.getTargetAddress(); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - public String getNAC() - { - return mNAC; - } - - public String getSystem() - { - return mSystem; - } - - public String getSiteAlias() - { - return mSiteAlias; - } - - public String getSite() - { - return mSite; - } - - public String getFromTalkgroup() - { - return mFromTalkgroup; - } - - public Alias getFromAlias() - { - return mFromAlias; - } - - public String getToTalkgroup() - { - return mToTalkgroup; - } - - public Alias getToAlias() - { - return mToAlias; - } - - public class ActiveCall - { - public static final long CALL_TIMEOUT = 2000; //milliseconds - public static final long CALL_DETECT_TIMEOUT = 5000; //milliseconds - - private P25CallEvent mEvent; - private long mLastUpdate; - private ThreadPoolManager mThreadPoolManager; - private AtomicBoolean mEnded = new AtomicBoolean( false ); - private long mTimeout; - private List mTalkgroups = new ArrayList(); - - public ActiveCall( ThreadPoolManager threadManager, P25CallEvent event ) - { - mThreadPoolManager = threadManager; - mEvent = event; - - if( event.getFromID() != null ) - { - mTalkgroups.add( event.getFromID() ); - } - - if( event.getToID() != null ) - { - mTalkgroups.add( event.getToID() ); - } - - mLastUpdate = System.currentTimeMillis(); - - mTimeout = event.getCallEventType() == - CallEventType.CALL_DETECT ? CALL_DETECT_TIMEOUT : CALL_TIMEOUT; - - mThreadPoolManager.scheduleOnce( new CallExpirationCheck( this ), - mTimeout, TimeUnit.MILLISECONDS ); - } - - public P25CallEvent getCallEvent() - { - return mEvent; - } - - public List getTalkgroups() - { - return mTalkgroups; - } - - public boolean matches( String channel, String id ) - { - return mEvent.getChannel() != null && - channel != null && - mEvent.getChannel().contentEquals( channel ) && - id != null && - mEvent.getToID().contentEquals( id ); - } - - /** - * Sets the last update timestamp to now - */ - public void update() - { - mLastUpdate = System.currentTimeMillis(); - } - - public void checkExpiration() - { - long now = System.currentTimeMillis(); - - if( !mEnded.get() ) - { - if( now > mLastUpdate + mTimeout ) - { - cleanup(); - } - else - { - mThreadPoolManager.scheduleOnce( new CallExpirationCheck( this ), - mTimeout, TimeUnit.MILLISECONDS ); - } - } - } - - public void end() - { - /* The expire check timer could fire while we're doing this, so we - * need to safely switch the flag and then run cleanup */ - if( mEnded.compareAndSet( false, true ) ) - { - cleanup(); - } - } - - private void cleanup() - { - setState( State.FADE ); - mCallEventModel.setEnd( mEvent ); - - if( mEvent.getChannel().contentEquals( mCurrentChannel ) ) - { - if( mCurrentActiveCall == this ) - { - mCurrentActiveCall = null; - } - } - else - { - mCallDetects.remove( this ); - } - } - - public long getExpiration() - { - return mLastUpdate + 2000; //2 seconds - } - } - - /** - * Timer to check active calls for call end - */ - public class CallExpirationCheck implements Runnable - { - private ActiveCall mActiveCall; - - public CallExpirationCheck( ActiveCall activeCall ) - { - mActiveCall = activeCall; - } - @Override - public void run() - { - mActiveCall.checkExpiration(); - } - } -} diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java deleted file mode 100644 index cfd96a66..00000000 --- a/src/decode/p25/P25Decoder.java +++ /dev/null @@ -1,406 +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 decode.p25; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.stream.ComplexTap; -import instrument.tap.stream.DibitTap; -import instrument.tap.stream.FloatTap; -import instrument.tap.stream.QPSKTap; - -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.real.RealSampleListener; -import source.Source.SampleType; -import source.tuner.frequency.FrequencyCorrectionControl; -import alias.AliasList; -import controller.ResourceManager; -import decode.Decoder; -import decode.DecoderType; -import decode.p25.audio.P25AudioOutput; -import dsp.filter.ComplexFIRFilter; -import dsp.filter.FilterFactory; -import dsp.filter.FloatFIRFilter; -import dsp.filter.Window.WindowType; -import dsp.filter.equalizer.CMAEqualizer; -import dsp.gain.ComplexFeedForwardGainControl; -import dsp.gain.DirectGainControl; -import dsp.nbfm.FMDiscriminator; -import dsp.psk.CQPSKDemodulator; -import dsp.psk.QPSKPolarSlicer; - -public class P25Decoder extends Decoder implements Instrumentable -{ - private final static Logger mLog = LoggerFactory.getLogger( P25Decoder.class ); - - private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- - - /* Instrumentation Taps */ - private static final String INSTRUMENT_COMPLEX_INPUT = "Tap Point: Complex Input"; - private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; - - private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; - private static final String INSTRUMENT_QPSK_DEMODULATOR_OUTPUT = "Tap Point: CQPSK Demodulator Output"; - private static final String INSTRUMENT_CQPSK_SLICER_OUTPUT = "Tap Point: CQPSK Slicer Output"; - - private static final String INSTRUMENT_REAL_INPUT = "Tap Point: Real Input"; - private static final String INSTRUMENT_DGC_OUTPUT = "Tap Point: DGC Output"; - private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; - private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; - - private List mAvailableTaps; - - /* Demods */ - private FMDiscriminator mFMDemodulator; - private CQPSKDemodulator mCQPSKDemodulator; - - /* Gain */ - private ComplexFeedForwardGainControl mAGC; - private DirectGainControl mDGC; - private CMAEqualizer mEqualizer; - - /* Filters */ - private ComplexFIRFilter mBasebandFilter; - private FloatFIRFilter mAudioFilter; - private C4FMSymbolFilter mSymbolFilter; - - /* Slicers */ - private C4FMSlicer mC4FMSlicer; - private QPSKPolarSlicer mCQPSKSlicer; - - /* Message framers and processors */ - private P25MessageFramer mMessageFramer; - private P25MessageProcessor mMessageProcessor; - - /* Audio */ - private P25AudioOutput mAudioOutput; - - private AliasList mAliasList; - private Modulation mModulation; - - public P25Decoder( ResourceManager resourceManager, - SampleType sampleType, - Modulation modulation, - AliasList aliasList ) - { - super( sampleType ); - mModulation = modulation; - - mAliasList = aliasList; - - mMessageProcessor = new P25MessageProcessor( mAliasList ); - mMessageProcessor.addMessageListener( this ); - - if( modulation == Modulation.CQPSK ) - { - /* Provide message framer with reference to the demod so that it - * can detect and issue corrections for costas phase lock errors */ - mCQPSKDemodulator = new CQPSKDemodulator(); - mMessageFramer = new P25MessageFramer( mAliasList, mCQPSKDemodulator ); - } - else - { - mMessageFramer = new P25MessageFramer( mAliasList ); - } - - mMessageFramer.setListener( mMessageProcessor ); - - /* Setup demodulation chains based on sample type (real or complex) and - * modulation (C4FM or CQPSK) */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - if( modulation == Modulation.CQPSK ) - { - mBasebandFilter = new ComplexFIRFilter( FilterFactory.getLowPass( - 48000, 7250, 8000, 60, WindowType.HANNING, true ), 1.0 ); - - this.addComplexListener( mBasebandFilter ); - -// mEqualizer = new CMAEqualizer( 1.0f, 0.005f ); -// mBasebandFilter.setListener( mEqualizer ); - - mAGC = new ComplexFeedForwardGainControl( 32 ); -// mEqualizer.setListener( mAGC ); - mBasebandFilter.setListener( mAGC ); - - mAGC.setListener( mCQPSKDemodulator ); - - mCQPSKSlicer = new QPSKPolarSlicer(); - mCQPSKDemodulator.setListener( mCQPSKSlicer ); - - mCQPSKSlicer.addListener( mMessageFramer ); - } - else /* C4FM */ - { - mBasebandFilter = new ComplexFIRFilter( FilterFactory.getLowPass( - 48000, 6750, 7500, 60, WindowType.HANNING, true ), 1.0 ); - - this.addComplexListener( mBasebandFilter ); - - mFMDemodulator = new FMDiscriminator( 1.0f ); - mBasebandFilter.setListener( mFMDemodulator ); - - /* Route output of the FM demod back to this channel so that we - * can process the output as if it were coming from any other - * real sample source */ - mFMDemodulator.setListener( getRealReceiver() ); - } - } - - /* Processing chain for real samples & demodulated complex samples */ - if( modulation == Modulation.C4FM ) - { - mAudioFilter = new FloatFIRFilter( FilterFactory.getLowPass( 48000, - 3000, 4000, 48, WindowType.HAMMING, true ), 1.0 ); - addRealSampleListener( mAudioFilter ); - - mDGC = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); - mAudioFilter.setListener( mDGC ); - - mFrequencyCorrection = new FrequencyCorrectionControl( - MAXIMUM_FREQUENCY_CORRECTION ); - - mSymbolFilter = new C4FMSymbolFilter( mDGC, mFrequencyCorrection ); - mDGC.setListener( mSymbolFilter ); - - mC4FMSlicer = new C4FMSlicer(); - mSymbolFilter.setListener( mC4FMSlicer ); - - mC4FMSlicer.addListener( mMessageFramer ); - } - - mAudioOutput = new P25AudioOutput( resourceManager ); - mMessageProcessor.addMessageListener( mAudioOutput ); - } - - public P25AudioOutput getAudioOutput() - { - return mAudioOutput; - } - - @Override - public DecoderType getType() - { - return DecoderType.P25_PHASE1; - } - - @Override - public List getTaps() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - if( mSourceSampleType == SampleType.COMPLEX ) - { - if( mCQPSKDemodulator != null ) - { - mAvailableTaps.addAll( mCQPSKDemodulator.getTaps() ); - } -// mAvailableTaps.add( new ComplexTap( INSTRUMENT_COMPLEX_INPUT, 0, 1.0f ) ); -// mAvailableTaps.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); - } - else - { - mAvailableTaps.add( new FloatTap( INSTRUMENT_REAL_INPUT, 0, 1.0f ) ); - } - - if( mModulation == Modulation.C4FM ) - { - mAvailableTaps.add( new FloatTap( INSTRUMENT_DGC_OUTPUT, 0, 1.0f ) ); - mAvailableTaps.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); - mAvailableTaps.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); - - if( mSymbolFilter != null ) - { - mAvailableTaps.addAll( mSymbolFilter.getTaps() ); - } - } - else - { - mAvailableTaps.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); - mAvailableTaps.add( new QPSKTap( INSTRUMENT_QPSK_DEMODULATOR_OUTPUT, 0, 1.0f ) ); - mAvailableTaps.add( new DibitTap( INSTRUMENT_CQPSK_SLICER_OUTPUT, 0, 0.1f ) ); - } - } - - return mAvailableTaps; - } - - @Override - public void addTap( Tap tap ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.addTap( tap ); - } - - if( mCQPSKDemodulator != null ) - { - mCQPSKDemodulator.addTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_COMPLEX_INPUT: - addComplexListener( (ComplexTap)tap ); - break; - case INSTRUMENT_BASEBAND_FILTER_OUTPUT: - ComplexTap baseband = (ComplexTap)tap; - mBasebandFilter.setListener( baseband ); - baseband.setListener( mAGC ); - break; - case INSTRUMENT_AGC_OUTPUT: - ComplexTap agcSymbol = (ComplexTap)tap; - mAGC.setListener( agcSymbol ); - agcSymbol.setListener( mCQPSKDemodulator ); - break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: - QPSKTap qpsk = (QPSKTap)tap; - mCQPSKDemodulator.setListener( qpsk ); - qpsk.setListener( mCQPSKSlicer ); - break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.addListener( (DibitTap)tap ); - break; - case INSTRUMENT_REAL_INPUT: - FloatTap inputAGC = (FloatTap)tap; - removeRealListener( mDGC ); - addRealSampleListener( inputAGC ); - inputAGC.setListener( mDGC ); - break; - case INSTRUMENT_DGC_OUTPUT: - FloatTap dgcSymbol = (FloatTap)tap; - mDGC.setListener( dgcSymbol ); - dgcSymbol.setListener( mSymbolFilter ); - break; - case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: - FloatTap symbolSlicer = (FloatTap)tap; - mSymbolFilter.setListener( symbolSlicer ); - symbolSlicer.setListener( mC4FMSlicer ); - break; - case INSTRUMENT_C4FM_SLICER_OUTPUT: - DibitTap slicer = (DibitTap)tap; - - if( mC4FMSlicer != null ) - { - mC4FMSlicer.addListener( slicer ); - } - - if( mCQPSKSlicer != null ) - { - mCQPSKSlicer.addListener( slicer ); - } - break; - } - } - - @Override - public void removeTap( Tap tap ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.removeTap( tap ); - } - - if( mCQPSKDemodulator != null ) - { - mCQPSKDemodulator.removeTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_COMPLEX_INPUT: - removeComplexListener( (ComplexTap)tap ); - break; - case INSTRUMENT_BASEBAND_FILTER_OUTPUT: - mBasebandFilter.setListener( mAGC ); - break; - case INSTRUMENT_AGC_OUTPUT: - mAGC.setListener( mCQPSKDemodulator ); - break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: - mCQPSKDemodulator.setListener( mCQPSKSlicer ); - break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.removeListener( (DibitTap)tap ); - break; - case INSTRUMENT_REAL_INPUT: - FloatTap inputAGC = (FloatTap)tap; - removeRealListener( inputAGC ); - addRealSampleListener( mDGC ); - break; - case INSTRUMENT_DGC_OUTPUT: - mDGC.setListener( mSymbolFilter ); - break; - case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: - mSymbolFilter.setListener( mC4FMSlicer ); - break; - case INSTRUMENT_C4FM_SLICER_OUTPUT: - DibitTap slicerTap = (DibitTap)tap; - - if( mC4FMSlicer != null ) - { - mC4FMSlicer.removeListener( slicerTap ); - } - break; - } - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - throw new IllegalArgumentException( "unfiltered real sample provider " - + "not implemented in P25 Decoder" ); - } - - public enum Modulation - { - CQPSK( "LSM SIMULCAST", "LSM" ), - C4FM( "C4FM", "C4FM" ); - - private String mLabel; - private String mShortLabel; - - private Modulation( String label, String shortLabel ) - { - mLabel = label; - mShortLabel = shortLabel; - } - - public String getLabel() - { - return mLabel; - } - - public String getShortLabel() - { - return mShortLabel; - } - - public String toString() - { - return getLabel(); - } - }; -} diff --git a/src/decode/p25/audio/IMBETargetDataLine.java b/src/decode/p25/audio/IMBETargetDataLine.java deleted file mode 100644 index 041e233f..00000000 --- a/src/decode/p25/audio/IMBETargetDataLine.java +++ /dev/null @@ -1,273 +0,0 @@ -package decode.p25.audio; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Control; -import javax.sound.sampled.Control.Type; -import javax.sound.sampled.DataLine; -import javax.sound.sampled.LineListener; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.TargetDataLine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import audio.AudioFormats; - -public class IMBETargetDataLine implements TargetDataLine -{ - private final static Logger mLog = - LoggerFactory.getLogger( IMBETargetDataLine.class ); - - public static final int BUFFER_SIZE = 18; - - public byte[] mFrame; - - /** - * IMBE target dataline provides a java audio system compatible interface to - * ingest and buffer raw IMBE audio frames and expose a standard audio - * system interface for follow-on audio processors to consume the output audio - * stream provided by this interface. - */ - public IMBETargetDataLine() - { - } - - @Override - public void open( AudioFormat format, int bufferSize ) - throws LineUnavailableException - { - if( format != AudioFormats.IMBE_AUDIO_FORMAT ) - { - throw new LineUnavailableException( "Unsupported format" ); - } - } - - @Override - public void open() throws LineUnavailableException - { - open( AudioFormats.IMBE_AUDIO_FORMAT, BUFFER_SIZE ); - } - - @Override - public void close() - { - } - - @Override - public boolean isOpen() - { - return true; - } - - @Override - public void open( AudioFormat format ) throws LineUnavailableException - { - open( format, BUFFER_SIZE ); - } - - @Override - public int read( byte[] buffer, int offset, int length ) - { - if( mFrame != null ) - { - System.arraycopy( mFrame, 0, buffer, 0, mFrame.length ); - - return mFrame.length; - } - - return 0; - } - -// private int getBytesAvailable() -// { -// return 1920; -// return mFrame.length; -// } - - /** - * Primary inject point for submitting IMBE frame data into the audio - * system via this data line - */ - public void receive( byte[] data ) - { - mFrame = data; - } - - /** - * Not implemented - */ - @Override - public void drain() - { - } - - /** - * Flushes (deletes) any buffered audio frames. If a frame is currently - * being processed it will continue and a stop event will be broadcast - * after the current frame has been dispatched. - */ - @Override - public void flush() - { - mFrame = null; - } - - /** - * If the line is open, sets the running state to true and attempts to - * retrieve a frame from the buffer. If successful, a line start event is - * broadcast. If no frames are currently buffered, then a line start event - * will be broadcast once the first frame is received. - * - * If the line is closed, or if the running state is already set to true, - * then the start() is ignored. - */ - @Override - public void start() - { - } - - /** - * Stops this data line, sets running and active states to false and purges - * any currently buffered data frames. Note: the line will remain open - * until close() is invoked and incoming frame data will continue to buffer. - */ - @Override - public void stop() - { - } - - /** - * Indicates if this line us currently opened, but does not reflect if the - * line is currently streaming data. Use the isActive() method to determine - * streaming status. - */ - @Override - public boolean isRunning() - { - return true; - } - - /** - * Indicates if this line is currently streaming data - */ - @Override - public boolean isActive() - { - return true; - } - - /** - * IMBE AudioFormat produced by this target data line. - */ - @Override - public AudioFormat getFormat() - { - return AudioFormats.IMBE_AUDIO_FORMAT; - } - - /** - * Size of the internal buffer in bytes. This method will not return the - * true size of the buffer if this data line was constructed with a buffer - * size other than the default (250 frames) buffer size. - */ - @Override - public int getBufferSize() - { - return 18; - } - - /** - * Number of bytes of imbe frame data available. Value will be the number - * of buffered frames times 18 bytes. - */ - @Override - public int available() - { - return 18; - } - - /** - * Current number of frames provided by this data line since it was opened. - * Counter rolls over once it exceeds the max integer value. - */ - @Override - public int getFramePosition() - { - return 0; - } - - /** - * Current number of frames provided by this data line since it was opened - */ - @Override - public long getLongFramePosition() - { - return 0; - } - - /** - * Number of microseconds worth of data that has been sourced by this - * target data line. Returns the frame counter times 20,000 microseconds. - */ - @Override - public long getMicrosecondPosition() - { - return 0; - } - - /** - * Not implemented - */ - @Override - public float getLevel() - { - return AudioSystem.NOT_SPECIFIED; - } - - @Override - public javax.sound.sampled.Line.Info getLineInfo() - { - return new DataLine.Info( IMBETargetDataLine.class, - AudioFormats.IMBE_AUDIO_FORMAT ); - } - - /** - * No controls are implemented for this data line - */ - @Override - public Control[] getControls() - { - return null; - } - - /** - * No controls are implemented for this data line - */ - @Override - public boolean isControlSupported( Type control ) - { - return false; - } - - /** - * No controls are implemented for this data line - */ - @Override - public Control getControl( Type control ) - { - return null; - } - - @Override - public void addLineListener( LineListener listener ) - { - // TODO Auto-generated method stub - } - - @Override - public void removeLineListener( LineListener listener ) - { - // TODO Auto-generated method stub - } -} diff --git a/src/decode/p25/audio/P25AudioCodecTest.java b/src/decode/p25/audio/P25AudioCodecTest.java deleted file mode 100644 index c35f330d..00000000 --- a/src/decode/p25/audio/P25AudioCodecTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package decode.p25.audio; - -import java.util.Iterator; -import java.util.ServiceLoader; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.TargetDataLine; -import javax.sound.sampled.spi.FormatConversionProvider; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import audio.AudioFormats; - -public class P25AudioCodecTest -{ - private final static Logger mLog = LoggerFactory.getLogger( P25AudioCodecTest.class ); - - public static void main( String[] args ) - { - mLog.debug( "Starting ..." ); - AudioFormat from = AudioFormats.IMBE_AUDIO_FORMAT; - AudioFormat to = AudioFormats.PCM_SIGNED_8KHZ_16BITS; - - ServiceLoader loader = ServiceLoader.load( FormatConversionProvider.class ); - - Iterator it = loader.iterator(); - - while( it.hasNext() ) - { - mLog.debug( "Provider:" + it.next().getClass() ); - } - - - -// try -// { -// TargetDataLine tdl = AudioSystem.getTargetDataLine( from ); -// -// mLog.debug( "We got a tdl ..." ); -// } -// catch ( Exception e ) -// { -// e.printStackTrace(); -// } - - mLog.debug( "Finished" ); - } -} diff --git a/src/decode/p25/audio/P25AudioOutput.java b/src/decode/p25/audio/P25AudioOutput.java deleted file mode 100644 index da4bf084..00000000 --- a/src/decode/p25/audio/P25AudioOutput.java +++ /dev/null @@ -1,154 +0,0 @@ -package decode.p25.audio; - -import jmbe.iface.AudioConversionLibrary; -import jmbe.iface.AudioConverter; -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import audio.AudioFormats; -import audio.AudioPacket; -import audio.IAudioOutput; -import audio.MonoAudioOutput; -import audio.SquelchListener; -import audio.inverted.AudioType; -import audio.inverted.IAudioTypeListener; -import controller.ResourceManager; -import decode.p25.message.ldu.LDUMessage; -import decode.p25.message.tdu.TDUMessage; -import decode.p25.message.tdu.lc.TDULinkControlMessage; - -/** - * P25 Phase 1 IMBE Audio Output converter. Processes 18-byte IMBE audio frames - * and produces PCM 48k MONO 16-bit sample output. - * - * Note: this class depends on the external JMBE library. - */ -public class P25AudioOutput implements IAudioOutput, Listener, - SquelchListener, IAudioTypeListener -{ - private final static Logger mLog = LoggerFactory.getLogger( P25AudioOutput.class ); - - private static final String IMBE_CODEC = "IMBE"; - - private boolean mCanConvertAudio = false; - private boolean mEncryptedAudio = false; - - private AudioConverter mAudioConverter; - - private MonoAudioOutput mAudioOutput = new MonoAudioOutput(); - - public P25AudioOutput( ResourceManager resourceManager ) - { - loadConverter(); - } - - /** - * Primary inject point for p25 imbe audio frame messages. Each LDU audio - * message contains 9 imbe voice frames. If the audio is not encrypted, - * we insert each audio frame asynchronously into the audio converter. - */ - public void receive( Message message ) - { - if( mCanConvertAudio ) - { - if( message instanceof LDUMessage ) - { - LDUMessage ldu = (LDUMessage)message; - - /* If we detect a valid LDU message with the encryption flag - * set to true, we toggle the encrypted audio state until we - * receive the first TDU or TDULC message, then reset it. */ - if( ldu.isValid() && ldu.isEncrypted() ) - { - mEncryptedAudio = true; - } - - if( !mEncryptedAudio && mAudioConverter != null ) - { - for( byte[] frame: ((LDUMessage)message).getIMBEFrames() ) - { - byte[] audio = mAudioConverter.convert( frame ); - - mAudioOutput.receive( new AudioPacket( "P25 Audio", audio, 1 ) ); - } - } - } - else if( message instanceof TDUMessage || message instanceof TDULinkControlMessage ) - { - mEncryptedAudio = false; - } - } - } - - /** - * Loads audio frame processing chain. Constructs an imbe targetdataline - * to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion - * stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper. - */ - private void loadConverter() - { - AudioConversionLibrary library = null; - - try - { - @SuppressWarnings( "rawtypes" ) - Class temp = Class.forName( "jmbe.JMBEAudioLibrary" ); - - library = (AudioConversionLibrary)temp.newInstance(); - - mAudioConverter = library.getAudioConverter( IMBE_CODEC, - AudioFormats.PCM_SIGNED_48KHZ_16BITS ); - - if( mAudioConverter != null ) - { - mCanConvertAudio = true; - mLog.info( "JMBE audio conversion library successfully loaded" - + " - P25 audio will be available" ); - } - else - { - mLog.info( "JMBE audio conversion library NOT FOUND" ); - } - } - catch ( ClassNotFoundException e1 ) - { - mLog.error( "Couldn't find/load JMBE audio conversion library" ); - } - catch ( InstantiationException e1 ) - { - mLog.error( "Couldn't instantiate JMBE audio conversion library class" ); - } - catch ( IllegalAccessException e1 ) - { - mLog.error( "Couldn't load JMBE audio conversion library due to " - + "security restrictions" ); - } - } - - @Override - public void setAudioPlaybackEnabled( boolean enabled ) - { - mAudioOutput.setMuted( !enabled ); - } - - @Override - public void dispose() - { - mAudioOutput.dispose(); - } - - @Override - public void setSquelch( SquelchState state ) - { - /* not implemented */ - } - - @Override - public void setAudioType( AudioType type ) - { - //Not implemented - } -} \ No newline at end of file diff --git a/src/decode/passport/PassportActivitySummary.java b/src/decode/passport/PassportActivitySummary.java deleted file mode 100644 index 055ca424..00000000 --- a/src/decode/passport/PassportActivitySummary.java +++ /dev/null @@ -1,233 +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 decode.passport; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class PassportActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private AliasList mAliasList; - 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(); - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public PassportActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof PassportMessage ) - { - PassportMessage passport = ((PassportMessage)message); - - if( passport.isValid() ) - { - switch( passport.getMessageType() ) - { - case CA_STRT: - mSiteLCNs.put( passport.getLCN(), - passport.getLCNFrequencyFormatted() ); - case CA_ENDD: - String talkgroup = - String.valueOf( passport.getTalkgroupID() ); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - break; - case ID_RDIO: - String min = passport.getMobileID(); - - if( min != null ) - { - mMobileIDs.add( min ); - } - break; - case SY_IDLE: - if( passport.getFree() != 0 ) - { - mNeighborLCNs.put( passport.getFree(), - passport.getFreeFrequencyFormatted() ); - } - break; - default: - break; - } - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tPassport\n\n" ); - sb.append( "Site Channels\n" ); - - if( mSiteLCNs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - ArrayList channels = new ArrayList( mSiteLCNs.keySet() ); - Collections.sort( channels ); - - for( Integer channel: channels ) - { - sb.append( " " + channel ); - sb.append( "\t" + mSiteLCNs.get( channel ) ); - sb.append( "\n" ); - } - } - - sb.append( "\nNeighbor Channels\n" ); - - if( mNeighborLCNs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - ArrayList channels = new ArrayList( mNeighborLCNs.keySet() ); - Collections.sort( channels ); - - for( Integer channel: channels ) - { - sb.append( " " + channel ); - sb.append( "\t" + mNeighborLCNs.get( channel ) ); - sb.append( "\n" ); - } - } - - sb.append( "\nTalkgroups\n" ); - - if( mTalkgroups.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while( it.hasNext() ) - { - String tgid = it.next(); - - sb.append( " " ); - sb.append( tgid ); - sb.append( " " ); - - Alias alias = mAliasList.getTalkgroupAlias( tgid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nMobile ID Numbers\n" ); - - if( mMobileIDs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mMobileIDs.iterator(); - - while( it.hasNext() ) - { - String min = it.next(); - - sb.append( " " ); - sb.append( min ); - sb.append( " " ); - - Alias alias = mAliasList.getMobileIDNumberAlias( min ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } - - 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; - } - } -} diff --git a/src/decode/passport/PassportChannelState.java b/src/decode/passport/PassportChannelState.java deleted file mode 100644 index f64d34ea..00000000 --- a/src/decode/passport/PassportChannelState.java +++ /dev/null @@ -1,369 +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 decode.passport; - -import gui.SDRTrunk; - -import java.util.HashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import audio.SquelchListener; -import audio.inverted.AudioType; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; - -public class PassportChannelState extends ChannelState -{ - private final static Logger mLog = - LoggerFactory.getLogger( PassportChannelState.class ); - - private String mTalkgroup; - private Alias mTalkgroupAlias; - private String mMobileID; - private Alias mMobileIDAlias; - private int mChannelNumber; - private int mSiteNumber; - private PassportBand mSiteBand; - private PassportActivitySummary mActivitySummary; - private HashMap mActiveCalls = new HashMap(); - - public PassportChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - - mActivitySummary = new PassportActivitySummary( aliasList ); - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - @SuppressWarnings( "incomplete-switch" ) - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof PassportMessage ) - { - mActivitySummary.receive( message ); - - PassportMessage passport = (PassportMessage)message; - - if( passport.isValid() ) - { - switch( passport.getMessageType() ) - { - case CA_STRT: - if( mChannelNumber == 0 ) - { - mChannelNumber = passport.getLCN(); - } - - if( passport.getLCN() == mChannelNumber ) - { - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - - if( mTalkgroupAlias != null ) - { - try - { - if( mTalkgroupAlias.hasAudioType() ) - { - mAudioTypeListener.setAudioType( - mTalkgroupAlias.getAudioType() ); - } - } - catch( Exception e ) - { - mLog.error( "Error setting audio for " - + "passport call", e ); - } - } - - PassportCallEvent current = - getCurrentPassportCallEvent(); - - /* 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 && !current.addMessage( passport ) ) - { - if( current.isValid() ) - { - final PassportCallEvent endEvent = - getCurrentPassportCallEvent(); - - mCallEventModel.setEnd( endEvent ); - } - else - { - mCallEventModel.remove( current ); - } - - current = null; - setCurrentCallEvent( null ); - } - - if( current == null ) - { - mCurrentCallEvent = new PassportCallEvent - .Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( String.valueOf( mChannelNumber ) ) - .frequency( passport.getLCNFrequency() ) - .to( String.valueOf( passport.getTalkgroupID() ) ) - .build(); - - mCallEventModel.add( mCurrentCallEvent ); - } - - State state = getState(); - - if( state == State.IDLE || state == State.CALL ) - { - setState( State.CALL ); - } - else if( state == State.DATA ) - { - setState( 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 ); - - PassportCallEvent event = new PassportCallEvent - .Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( String.valueOf( lcn ) ) - .details( "Site: " + passport.getSite() ) - .frequency( passport.getLCNFrequency() ) - .to( tg ) - .build(); - - mCallEventModel.add( event ); - } - } - break; - case DA_STRT: -//TODO: add channel number checking and log a call event if on another site - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - setState( State.DATA ); - break; - case CA_ENDD: - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - fade( CallEventType.CALL_END ); - break; - case ID_RDIO: - setMobileID( passport.getMobileID() ); - setMobileIDAlias( passport.getMobileIDAlias() ); - - final String mid = passport.getMobileID(); - - final PassportCallEvent event = - getCurrentPassportCallEvent(); - - if( event != null && mid != null ) - { - mCallEventModel.setFromID( event, mid ); - } - - setState( State.CALL ); - break; - case RA_REGI: - mCallEventModel.add( new PassportCallEvent - .Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .channel( String.valueOf( passport.getLCN() ) ) - .frequency( passport.getLCNFrequency() ) - .to( passport.getToID() ) - .build() ); -//TODO: add logic to recognize the register event and suppress the subsequent -//call messages, but capture the group assignments. - setState( State.DATA ); - break; - case SY_IDLE: - PassportBand band = passport.getSiteBand(); - - if( mSiteBand == null || mSiteBand != band ) - { - mSiteBand = band; - - mSiteNumber = passport.getSite(); - - long frequency = mProcessingChain.getChannel() - .getTunerChannel().getFrequency(); - - mChannelNumber = mSiteBand.getChannel( frequency ); - - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - default: - break; - } - } - } - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* - * We can receive multiple call tear-down messages -- only post a call - * end event for the one that can change the state to fade - */ - if( getState().canChangeTo( State.FADE ) ) - { - final PassportCallEvent current = getCurrentPassportCallEvent(); - - if( current != null ) - { - mCallEventModel.setEnd( current ); - } - } - - setCurrentCallEvent( null ); - - mActiveCalls.clear(); - - super.fade( type ); - } - - public PassportCallEvent getCurrentPassportCallEvent() - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - return (PassportCallEvent)current; - } - - return null; - } - - /** - * Make the ConventionalChannelState always unsquelched - */ - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - } - - public void reset() - { - mAudioTypeListener.setAudioType( AudioType.NORMAL ); - - mCurrentCallEvent = null; - - setTalkgroup( null ); - setTalkgroupAlias( null ); - - setMobileID( null ); - setMobileIDAlias( null ); - - super.reset(); - } - - public int getChannelNumber() - { - return mChannelNumber; - } - - public int getSiteNumber() - { - return mSiteNumber; - } - - public String getTalkgroup() - { - return mTalkgroup; - } - - public void setTalkgroup( String talkgroup ) - { - mTalkgroup = talkgroup; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public Alias getTalkgroupAlias() - { - return mTalkgroupAlias; - } - - public void setTalkgroupAlias( Alias alias ) - { - mTalkgroupAlias = alias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - - public String getMobileID() - { - return mMobileID; - } - - public void setMobileID( String id ) - { - mMobileID = id; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public Alias getMobileIDAlias() - { - return mMobileIDAlias; - } - - public void setMobileIDAlias( Alias alias ) - { - mMobileIDAlias = alias; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } -} diff --git a/src/decode/passport/PassportDecoder.java b/src/decode/passport/PassportDecoder.java deleted file mode 100644 index 8293123e..00000000 --- a/src/decode/passport/PassportDecoder.java +++ /dev/null @@ -1,165 +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 decode.passport; - -import instrument.Instrumentable; -import instrument.tap.Tap; - -import java.util.ArrayList; -import java.util.List; - -import sample.real.RealSampleListener; -import source.Source.SampleType; -import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import bits.MessageFramer; -import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.fsk.LTRFSKDecoder; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class PassportDecoder extends Decoder implements Instrumentable -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - public static final int sPASSPORT_MESSAGE_LENGTH = 68; - public static final int sPASSPORT_SYNC_LENGTH = 9; - private FilteringNBFMDemodulator mNBFMDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDecoder mPassportFSKDecoder; - private MessageFramer mPassportMessageFramer; - private PassportMessageProcessor mPassportMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "Passport Decoder Audio Output" ); - - private List mAvailableTaps; - - public PassportDecoder( DecodeConfiguration config, - SampleType sampleType, - AliasList aliasList ) - { - super( config, sampleType ); - - /** - * Only setup a demod chain if we're receiving complex samples. If - * we're receiving demodulated samples, they'll be handled the same - * was as we handle the output of the demodulator. - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - /** - * The Decoder super class is both a Complex listener and a Short - * listener. So, we add the demod to listen to the incoming - * quadrature samples, and we wire the output of the demod right - * back to this class, so we can receive the demodulated output - * to process - */ - mNBFMDemodulator = new FilteringNBFMDemodulator(); - this.addComplexListener( mNBFMDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mNBFMDemodulator.addListener( mDCRemovalFilter ); - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - mPassportFSKDecoder = - new LTRFSKDecoder(); - - /** - * The DC removal filter will impact performance of the LTRFSKDemod, - * so we get the samples straight from the NBFM demod - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mNBFMDemodulator.addListener( mPassportFSKDecoder ); - } - else - { - addRealSampleListener( mPassportFSKDecoder ); - } - - - mPassportMessageFramer = - new MessageFramer( SyncPattern.PASSPORT.getPattern(), - sPASSPORT_MESSAGE_LENGTH ); - - mPassportFSKDecoder.addListener( mPassportMessageFramer ); - - mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); - mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); - mPassportMessageProcessor.addMessageListener( this ); - - addRealSampleListener( mAudioOutput ); - } - - @Override - public DecoderType getType() - { - return DecoderType.PASSPORT; - } - - @Override - public List getTaps() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - mAvailableTaps.addAll( mPassportFSKDecoder.getTaps() ); - } - - return mAvailableTaps; - } - - @Override - public void addTap( Tap tap ) - { - mPassportFSKDecoder.addTap( tap ); - } - - @Override - public void removeTap( Tap tap ) - { - mPassportFSKDecoder.removeTap( tap ); - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( listener ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/tait/Tait1200MessageFilter.java b/src/decode/tait/Tait1200MessageFilter.java deleted file mode 100644 index 291a0e4c..00000000 --- a/src/decode/tait/Tait1200MessageFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package decode.tait; - -import java.util.Collections; -import java.util.List; - -import message.Message; -import filter.Filter; -import filter.FilterElement; - -public class Tait1200MessageFilter extends Filter -{ - - public Tait1200MessageFilter() - { - super( "Tait-1200 Message Filter" ); - } - - @Override - public boolean passes( Message message ) - { - return mEnabled && canProcess( message ); - } - - @Override - public boolean canProcess( Message message ) - { - return message instanceof Tait1200GPSMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/dsp/am/AMDemodulator.java b/src/dsp/am/AMDemodulator.java index 64cfc62b..261adc80 100644 --- a/src/dsp/am/AMDemodulator.java +++ b/src/dsp/am/AMDemodulator.java @@ -1,45 +1,22 @@ package dsp.am; -import sample.Listener; -import sample.complex.ComplexSample; -import sample.real.RealSampleListener; -import sample.real.RealSampleProvider; - -public class AMDemodulator implements Listener, RealSampleProvider +public abstract class AMDemodulator { - private RealSampleListener mListener; - - public AMDemodulator() + private float mGain; + + public AMDemodulator( float gain ) + { + mGain = gain; + } + + public float demodulate( float inphase, float quadrature ) { + return (float)Math.sqrt( ( inphase * inphase ) + + ( quadrature * quadrature ) ) * mGain; } - @Override - public void receive( ComplexSample sample ) - { - float demodulated = (float)Math.sqrt( ( sample.real() * - sample.real() ) + - ( sample.imaginary() * - sample.imaginary() ) ); - - if( mListener != null ) - { - mListener.receive( demodulated * 500.0f ); - } - } - - /** - * Adds a listener to receive demodulated samples - */ - public void setListener( RealSampleListener listener ) - { - mListener = listener; - } - - /** - * Removes a listener from receiving demodulated samples - */ - public void removeListener( RealSampleListener listener ) - { - mListener = null; - } + public void setGain( float gain ) + { + mGain = gain; + } } diff --git a/src/decode/tait/Tait1200ActivitySummary.java b/src/dsp/am/AMDemodulator_CB.java similarity index 52% rename from src/decode/tait/Tait1200ActivitySummary.java rename to src/dsp/am/AMDemodulator_CB.java index 5402b9b8..543dff37 100644 --- a/src/decode/tait/Tait1200ActivitySummary.java +++ b/src/dsp/am/AMDemodulator_CB.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,46 +15,55 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package dsp.am; -import message.Message; import sample.Listener; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; +import sample.complex.ComplexBuffer; +import sample.real.RealBuffer; -public class Tait1200ActivitySummary implements ActivitySummaryProvider, - Listener +public class AMDemodulator_CB extends AMDemodulator implements Listener { - private AliasList mAliasList; - - public Tait1200ActivitySummary( AliasList list ) + private Listener mListener; + + public AMDemodulator_CB( float gain ) { - mAliasList = list; + super( gain ); } + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] demodulated = new float[ half ]; + + for( int x = 0; x < half; x++ ) + { + int index = x * 2; + + demodulated[ x ] = demodulate( samples[ index ], samples[ index + 1 ] ); + } + + mListener.receive( new RealBuffer( demodulated ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } + public void dispose() { + mListener = null; } - - @Override - public void receive( Message message ) - { - if( message instanceof Tait1200GPSMessage ) - { - Tait1200GPSMessage tait = ((Tait1200GPSMessage)message); - - //Do something here in the future - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tTait-1200I\n\n" ); - - return sb.toString(); - } } diff --git a/src/dsp/filter/ComplexCICDecimate.java b/src/dsp/filter/ComplexCICDecimate.java index ae9f2c41..f82fde74 100644 --- a/src/dsp/filter/ComplexCICDecimate.java +++ b/src/dsp/filter/ComplexCICDecimate.java @@ -19,7 +19,7 @@ package dsp.filter; import sample.Listener; import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; import dsp.filter.Window.WindowType; /** @@ -30,7 +30,7 @@ import dsp.filter.Window.WindowType; * Note: comb delay is fixed at 1 for this filter. */ public class ComplexCICDecimate extends ComplexFilter - implements Provider + implements Provider { private Integrator[] mIntegrator; private Comb[] mComb; @@ -99,7 +99,7 @@ public class ComplexCICDecimate extends ComplexFilter * Input method for this filter to receive complex samples. */ @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mIntegrator[ 0 ].receive( (double)sample.left(), (double)sample.right() ); } @@ -108,7 +108,7 @@ public class ComplexCICDecimate extends ComplexFilter * Sets the listener to receive the output of this CIC filter */ @Override - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mOutput.setListener( listener ); } @@ -117,13 +117,13 @@ public class ComplexCICDecimate extends ComplexFilter * Removes the listener from receiving the output of this CIC filter */ @Override - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mOutput.removeListener( listener ); } @Override - public Listener getListener() + public Listener getListener() { return mOutput.getListener(); } @@ -260,11 +260,11 @@ public class ComplexCICDecimate extends ComplexFilter { mStageCount = stageCount; mDecimation = decimation; - mCleanupFilter = new ComplexFIRFilter( FilterFactory + mCleanupFilter = new ComplexFIRFilterOld( FilterFactory .getCICCleanupFilter( outputSampleRate, 12000, 60, - WindowType.BLACKMAN ), 1.0d ); + WindowType.BLACKMAN ), 1.0f ); setGain(); } @@ -278,7 +278,7 @@ public class ComplexCICDecimate extends ComplexFilter public void receive( double left, double right ) { mCleanupFilter.receive( - new ComplexSample( (float)( left * mGain ), + new Complex( (float)( left * mGain ), (float)( right * mGain ) ) ); } @@ -294,7 +294,7 @@ public class ComplexCICDecimate extends ComplexFilter /** * Adds a listener to receive output samples */ - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mCleanupFilter.setListener( listener ); } @@ -302,12 +302,12 @@ public class ComplexCICDecimate extends ComplexFilter /** * Removes the listener from receiving output samples */ - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mCleanupFilter.setListener( null ); } - public Listener getListener() + public Listener getListener() { return mCleanupFilter.getListener(); } diff --git a/src/dsp/filter/ComplexFIRFilter.java b/src/dsp/filter/ComplexFIRFilterOld.java similarity index 90% rename from src/dsp/filter/ComplexFIRFilter.java rename to src/dsp/filter/ComplexFIRFilterOld.java index 22f13482..84e9fc02 100644 --- a/src/dsp/filter/ComplexFIRFilter.java +++ b/src/dsp/filter/ComplexFIRFilterOld.java @@ -19,30 +19,30 @@ package dsp.filter; import java.util.ArrayList; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ComplexFIRFilter extends ComplexFilter +public class ComplexFIRFilterOld extends ComplexFilter { - private ArrayList mBuffer; + private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double[] mCoefficients; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; - private double mGain; + private float mGain; - public ComplexFIRFilter( double[] coefficients, double gain ) + public ComplexFIRFilterOld( float[] coefficients, float gain ) { mCoefficients = coefficients; - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); mBufferSize = mCoefficients.length; mGain = gain; //Fill the buffer with zero valued samples, so we don't have to check for null for( int x = 0; x < mCoefficients.length; x++ ) { - mBuffer.add( new ComplexSample( (float)0.0, (float)0.0 ) ); + mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); } generateIndexMap( mCoefficients.length ); @@ -57,7 +57,7 @@ public class ComplexFIRFilter extends ComplexFilter mIndexMap = null; } - public void receive( ComplexSample newSample ) + public void receive( Complex newSample ) { //Add the new sample to the buffer mBuffer.set( mBufferPointer, newSample ); @@ -96,7 +96,7 @@ public class ComplexFIRFilter extends ComplexFilter //We're almost finished ... apply gain, cast the doubles to floats and //send it on it's merry way - send( new ComplexSample( (float)( leftAccumulator * mGain ), + send( new Complex( (float)( leftAccumulator * mGain ), (float)( rightAccumulator * mGain ) ) ); } diff --git a/src/dsp/filter/ComplexFilter.java b/src/dsp/filter/ComplexFilter.java index d1b4f266..b5e08191 100644 --- a/src/dsp/filter/ComplexFilter.java +++ b/src/dsp/filter/ComplexFilter.java @@ -18,13 +18,14 @@ package dsp.filter; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public abstract class ComplexFilter implements Listener +@Deprecated +public abstract class ComplexFilter implements Listener { - private Listener mListener; + private Listener mListener; - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } @@ -34,7 +35,7 @@ public abstract class ComplexFilter implements Listener mListener = null; } - public Listener getListener() + public Listener getListener() { return mListener; } @@ -44,7 +45,7 @@ public abstract class ComplexFilter implements Listener return mListener != null; } - protected void send( ComplexSample sample ) + protected void send( Complex sample ) { if( mListener != null ) { diff --git a/src/dsp/filter/ComplexHalfBandFilter.java b/src/dsp/filter/ComplexHalfBandFilter.java index 49589286..a8dc266d 100644 --- a/src/dsp/filter/ComplexHalfBandFilter.java +++ b/src/dsp/filter/ComplexHalfBandFilter.java @@ -19,15 +19,15 @@ package dsp.filter; import java.util.ArrayList; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexHalfBandFilter extends ComplexFilter { - private ArrayList mBuffer; + private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -49,16 +49,16 @@ public class ComplexHalfBandFilter extends ComplexFilter * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public ComplexHalfBandFilter( Filters filter, double gain ) + public ComplexHalfBandFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); mBufferSize = mCoefficients.length; //Fill the buffer with zero valued samples for( int x = 0; x < mCoefficients.length; x++ ) { - mBuffer.add( new ComplexSample( (float)0.0, (float)0.0 ) ); + mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); } generateIndexMap( mCoefficients.length ); @@ -69,7 +69,7 @@ public class ComplexHalfBandFilter extends ComplexFilter * Calculate the filtered value by applying the coefficients against * the complex samples in mBuffer */ - public void receive( ComplexSample newSample ) + public void receive( Complex newSample ) { //Add the new sample to the buffer mBuffer.set( mBufferPointer, newSample ); @@ -115,7 +115,7 @@ public class ComplexHalfBandFilter extends ComplexFilter //We're almost finished ... apply gain, cast the doubles to floats and //send it on it's merry way - send( new ComplexSample( (float)( leftAccumulator * mGain ), + send( new Complex( (float)( leftAccumulator * mGain ), (float)( rightAccumulator * mGain ) ) ); } } diff --git a/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java b/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java index aa459ae6..72e40f06 100644 --- a/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java +++ b/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java @@ -20,16 +20,16 @@ package dsp.filter; import java.util.ArrayList; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexHalfBandNoDecimateFilter extends ComplexFilter { - private Listener mListener; - private ArrayList mBuffer; + private Listener mListener; + private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -51,16 +51,16 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public ComplexHalfBandNoDecimateFilter( Filters filter, double gain ) + public ComplexHalfBandNoDecimateFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); mBufferSize = mCoefficients.length; //Fill the buffer with zero valued samples for( int x = 0; x < mCoefficients.length; x++ ) { - mBuffer.add( new ComplexSample( (float)0.0, (float)0.0 ) ); + mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); } generateIndexMap( mCoefficients.length ); @@ -71,7 +71,7 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter * Calculate the filtered value by applying the coefficients against * the complex samples in mBuffer */ - public void receive( ComplexSample newSample ) + public void receive( Complex newSample ) { //Add the new sample to the buffer mBuffer.set( mBufferPointer, newSample ); @@ -86,8 +86,8 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double leftAccumulator = 0; - double rightAccumulator = 0; + float leftAccumulator = 0; + float rightAccumulator = 0; //Start with the center tap value leftAccumulator += mCoefficients[ mCenterCoefficient ] * @@ -108,10 +108,10 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).right() ); } - //We're almost finished ... apply gain, cast the doubles to floats and + //We're almost finished ... apply gain, cast the floats to floats and //send it on it's merry way - send( new ComplexSample( (float)( leftAccumulator * mGain ), - (float)( rightAccumulator * mGain ) ) ); + send( new Complex( ( leftAccumulator * mGain ), + ( rightAccumulator * mGain ) ) ); } /** @@ -180,9 +180,9 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter public static void main( String[] args ) { - double[] c = Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.mCoefficients; + float[] c = Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.mCoefficients; - double sum = 0; + float sum = 0; for( int x = 0; x < c.length; x++ ) { diff --git a/src/dsp/filter/ComplexPolyphaseDecimationFilter.java b/src/dsp/filter/ComplexPolyphaseDecimationFilter.java index e3e71e05..c2383050 100644 --- a/src/dsp/filter/ComplexPolyphaseDecimationFilter.java +++ b/src/dsp/filter/ComplexPolyphaseDecimationFilter.java @@ -18,7 +18,7 @@ package dsp.filter; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import buffer.ComplexTappedCircularBuffer; public class ComplexPolyphaseDecimationFilter extends ComplexFilter @@ -68,7 +68,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter * Inputs a new sample into the filter at the non-decimated rate */ @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mFilterPhases[ mPhasePointer++ ].receive( sample ); @@ -97,13 +97,13 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter for( int x = 0; x < mPhaseCount; x++ ) { - ComplexSample phaseSample = mFilterPhases[ x ].calculate(); + Complex phaseSample = mFilterPhases[ x ].calculate(); left += phaseSample.left(); right += phaseSample.right(); } - send( new ComplexSample( left, right ) ); + send( new Complex( left, right ) ); } } @@ -130,7 +130,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter /** * Implements a single phase of a poly-phase filter bank */ - public class FilterPhase implements Listener + public class FilterPhase implements Listener { private int mLength; private double[] mCoefficients; @@ -150,7 +150,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer.add( sample ); } @@ -159,7 +159,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter * Performs convolution by multiplying the buffer samples by their * corresponding filter coefficients and returns the sum */ - public ComplexSample calculate() + public Complex calculate() { /* Accumulators */ double left = 0.0d; @@ -171,7 +171,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter right += ( mBuffer.get( x ).right() * mCoefficients[ x ] ); } - return new ComplexSample( (float)left, (float)right ); + return new Complex( (float)left, (float)right ); } } } diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java index 0b65b419..5f35da33 100644 --- a/src/dsp/filter/FilterFactory.java +++ b/src/dsp/filter/FilterFactory.java @@ -27,10 +27,12 @@ import java.util.Set; import java.util.TreeSet; import org.jtransforms.fft.DoubleFFT_1D; +import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import dsp.filter.Window.WindowType; +import dsp.filter.cic.ComplexPrimeCICDecimate; public class FilterFactory { @@ -45,24 +47,24 @@ public class FilterFactory * @param window - to apply against the coefficients * @return */ - public static double[] getSinc( int sampleRate, - long frequency, - int length, - WindowType window ) + public static float[] getSinc( int sampleRate, + long frequency, + int length, + WindowType window ) { //Ensure we have an odd length assert( length % 2 == 0 ); //Get unity response array (one element longer to align with IDFT size) - double[] frequencyResponse = getUnityResponseArray( sampleRate, frequency, length + 1 ); + float[] frequencyResponse = getUnityResponseArray( sampleRate, frequency, length + 1 ); //Apply Inverse DFT against frequency response unity values, leaving the //IDFT bin results in the frequency response array - DoubleFFT_1D idft = new DoubleFFT_1D( length + 1 ); + FloatFFT_1D idft = new FloatFFT_1D( length + 1 ); idft.realInverseFull( frequencyResponse, false ); //Transfer the IDFT results to the return array - double[] coefficients = new double[ length ]; + float[] coefficients = new float[ length ]; int middleCoefficient = (int)( length / 2 ); //Bin 0 of the idft is our center coefficient @@ -91,9 +93,9 @@ public class FilterFactory * @param coefficients * @return */ - private static double[] normalize( double[] coefficients ) + private static float[] normalize( float[] coefficients ) { - double accumulator = 0; + float accumulator = 0; for( int x = 0; x < coefficients.length; x++ ) { @@ -124,51 +126,48 @@ public class FilterFactory * @param length * @return */ - public static double[] getUnityResponseArray( int sampleRate, - long frequency, - int length ) + public static float[] getUnityResponseArray( int sampleRate, + long frequency, + int length ) { - double[] unityArray = new double[ length * 2 ]; + float[] unityArray = new float[ length * 2 ]; int binCount = (int)( ( Math.round( - (double)frequency / (double)sampleRate * (double)length ) ) ); + (float)frequency / (float)sampleRate * (float)length ) ) ); -// if( length % 2 == 0 ) //even length -// { -// for( int x = 0; x < binCount; x++ ) -// { -// unityArray[ x ] = 1.0d; -// unityArray[ length - 1 - x ] = 1.0d; -// } -// } -// else //odd length -// { - unityArray[ 0 ] = 1.0d; + if( length % 2 == 0 ) //even length + { + for( int x = 0; x < binCount; x++ ) + { + unityArray[ x ] = 1.0f; + unityArray[ length - 1 - x ] = 1.0f; + } + } + else //odd length + { + unityArray[ 0 ] = 1.0f; for( int x = 1; x <= binCount; x++ ) { - unityArray[ x ] = 1.0d; - unityArray[ length - x ] = 1.0d; + unityArray[ x ] = 1.0f; + unityArray[ length - x ] = 1.0f; } -// } + } return unityArray; } - public static double[] getSine( double sampleRate, double frequency, int length ) + public static float[] getSine( float sampleRate, float frequency, int length ) { - //Ensure we have an odd length - assert( length % 2 == 0 ); + float[] retVal = new float[ length ]; - double[] retVal = new double[ length ]; - - double radianFrequency = 2.0D * Math.PI * ( frequency / sampleRate ); + float radianFrequency = (float)( 2.0d * Math.PI * ( frequency / sampleRate ) ); int middle = (int)( length / 2 ); for( int x = 0; x < middle; x++ ) { - double val = Math.sin( radianFrequency * x ); + float val = (float)Math.sin( radianFrequency * x ); retVal[ middle + x ] = val; retVal[ middle - x ] = -val; @@ -190,7 +189,7 @@ public class FilterFactory * ... * Index length - 1: same */ - public static double[] invert( double[] coefficients ) + public static float[] invert( float[] coefficients ) { for( int x = 1; x < coefficients.length; x += 2 ) { @@ -210,21 +209,21 @@ public class FilterFactory * @param windowType - window to apply against the generated coefficients * @return */ - public static double[] getLowPass( int sampleRate, - long cutoff, - int filterLength, - WindowType windowType ) + public static float[] getLowPass( int sampleRate, + long cutoff, + int filterLength, + WindowType windowType ) { if( filterLength % 2 == 0 ) //even length { - double[] values = getSinc( sampleRate, cutoff, filterLength + 2, windowType ); + float[] values = getSinc( sampleRate, cutoff, filterLength + 2, windowType ); //throw away the 0 index and the last index return Arrays.copyOfRange( values, 1, values.length - 2 ); } else { - double[] values = getSinc( sampleRate, cutoff, filterLength + 1, windowType ); + float[] values = getSinc( sampleRate, cutoff, filterLength + 1, windowType ); //throw away the 0 index return Arrays.copyOfRange( values, 1, values.length ); @@ -245,12 +244,12 @@ public class FilterFactory * - passFrequency < stopFrequency * - stopFrequency <= sampleRate/2 */ - public static double[] getLowPass( int sampleRate, - int passFrequency, - int stopFrequency, - int attenuation, - WindowType windowType, - boolean forceOddLength ) + public static float[] getLowPass( int sampleRate, + int passFrequency, + int stopFrequency, + int attenuation, + WindowType windowType, + boolean forceOddLength ) { if( stopFrequency < passFrequency || stopFrequency > ( sampleRate / 2 ) ) { @@ -288,10 +287,10 @@ public class FilterFactory * @param windowType - window to apply against the generated coefficients * @return */ - public static double[] getHighPass( int sampleRate, - long cutoff, - int filterLength, - WindowType windowType ) + public static float[] getHighPass( int sampleRate, + long cutoff, + int filterLength, + WindowType windowType ) { //Convert the high frequency cutoff to its low frequency cutoff //equivalent, so that when we generate the low pass filter, prior to @@ -304,12 +303,12 @@ public class FilterFactory windowType ) ); } - public static double[] getHighPass( int sampleRate, - long stopFrequency, - long passFrequency, - int attenuation, - WindowType windowType, - boolean forceOddLength ) + public static float[] getHighPass( int sampleRate, + long stopFrequency, + long passFrequency, + int attenuation, + WindowType windowType, + boolean forceOddLength ) { /* reverse the stop and pass frequency to get the low pass variant */ int tapCount = getTapCount( sampleRate, @@ -331,7 +330,7 @@ public class FilterFactory /** * Utility to log the arrays of doubles with line breaks */ - public static String arrayToString( double[] array, boolean breaks ) + public static String arrayToString( float[] array, boolean breaks ) { StringBuilder sb = new StringBuilder(); for( int x = 0; x < array.length; x++ ) @@ -574,10 +573,10 @@ public class FilterFactory * @param stageCount * @return */ - public static double[] getCICCleanupFilter( int outputSampleRate, - int passFrequency, - int attenuation, - WindowType window ) + public static float[] getCICCleanupFilter( int outputSampleRate, + int passFrequency, + int attenuation, + WindowType window ) { int taps = getTapCount( outputSampleRate, passFrequency, passFrequency + 1500, attenuation ); @@ -588,16 +587,16 @@ public class FilterFactory taps ++; } - double[] frequencyResponse = + float[] frequencyResponse = getCICResponseArray( outputSampleRate, passFrequency, taps ); //Apply Inverse DFT against frequency response unity values, leaving the //IDFT bin results in the frequency response array - DoubleFFT_1D idft = new DoubleFFT_1D( taps ); + FloatFFT_1D idft = new FloatFFT_1D( taps ); idft.realInverseFull( frequencyResponse, false ); //Transfer the IDFT results to the odd length return array - double[] coefficients = new double[ taps ]; + float[] coefficients = new float[ taps ]; int middleCoefficient = (int)( taps / 2 ); //Bin 0 of the idft is our center coefficient @@ -612,32 +611,32 @@ public class FilterFactory } //Apply the window against the coefficients - coefficients = Window.apply( window, coefficients ); +// coefficients = Window.apply( window, coefficients ); normalize( coefficients ); return coefficients; } - public static double[] getCICResponseArray( int sampleRate, + public static float[] getCICResponseArray( int sampleRate, int frequency, int length ) { - double[] cicArray = new double[ length * 2 ]; + float[] cicArray = new float[ length * 2 ]; int binCount = (int)( ( Math.round( - (double)frequency / (double)sampleRate * (double)length ) ) ); + (double)frequency / (double)sampleRate * 2.0d * (double)length ) ) ); - cicArray[ 0 ] = 1.0d; + cicArray[ 0 ] = 1.0f; - double unityResponse = Math.sin( 1.0d / (double)length ) / - ( 1.0d / (double)length ); + float unityResponse = (float)( Math.sin( 1.0d / (double)length ) / + ( 1.0d / (double)length ) ); for( int x = 1; x <= binCount; x++ ) { /* Calculate unity response plus amplification for droop */ - double compensated = 1.0d + ( unityResponse - - ( Math.sin( (double)x / (double)length ) / + float compensated = 1.0f + ( unityResponse - + (float)( Math.sin( (double)x / (double)length ) / ( (double)x / (double)length ) ) ); cicArray[ x ] = compensated; @@ -666,64 +665,64 @@ public class FilterFactory * * @return - filter coefficients */ - public static double[] getRootRaisedCosine( int samplesPerSymbol, - int symbols, - double alpha ) + public static float[] getRootRaisedCosine( int samplesPerSymbol, + int symbols, + float alpha ) { int taps = samplesPerSymbol * symbols + 1; - double scale = 0; + float scale = 0; - double[] coefficients = new double[ taps ]; + float[] coefficients = new float[ taps ]; for( int x = 0; x < taps; x++ ) { - double index = x - ( taps / 2 ); + float index = (float)x - ( (float)taps / 2.0f ); - double x1 = Math.PI * index / (double)samplesPerSymbol; - double x2 = 4.0 * alpha * index / (double)samplesPerSymbol; - double x3 = x2 * x2 - 1; + float x1 = (float)Math.PI * index / (float)samplesPerSymbol; + float x2 = 4.0f * alpha * index / (float)samplesPerSymbol; + float x3 = x2 * x2 - 1.0f; - double numerator, denominator; + float numerator, denominator; if( Math.abs( x3 ) >= 0.000001 ) { if( x != taps / 2 ) { - numerator = Math.cos( ( 1.0 + alpha ) * x1 ) + - Math.sin( ( 1.0 - alpha ) * x1 ) / - ( 4.0 * alpha * index / (double)samplesPerSymbol ); + numerator = (float)Math.cos( ( 1.0 + alpha ) * x1 ) + + (float)Math.sin( ( 1.0f - alpha ) * x1 ) / + ( 4.0f * alpha * index / (float)samplesPerSymbol ); } else { - numerator = Math.cos( ( 1.0 + alpha ) * x1 ) + - ( 1.0 - alpha ) * Math.PI / ( 4.0 * alpha ); + numerator = (float)Math.cos( ( 1.0f + alpha ) * x1 ) + + ( 1.0f - alpha ) * (float)Math.PI / ( 4.0f * alpha ); } - denominator = x3 * Math.PI; + denominator = x3 * (float)Math.PI; } else { - if( alpha == 1.0 ) + if( alpha == 1.0f ) { - coefficients[ x ] = -1.0; + coefficients[ x ] = -1.0f; continue; } - x3 = ( 1.0 - alpha ) * x1; - x2 = ( 1.0 + alpha ) * x1; + x3 = ( 1.0f - alpha ) * x1; + x2 = ( 1.0f + alpha ) * x1; - numerator = ( Math.sin( x2 ) * ( 1.0 + alpha ) * Math.PI - + numerator = (float)( Math.sin( x2 ) * ( 1.0f + alpha ) * Math.PI - Math.cos( x3 ) * ( ( 1.0 - alpha ) * Math.PI * (double)samplesPerSymbol ) / ( 4 * alpha * index ) + Math.sin( x3 ) * (double)samplesPerSymbol * (double)samplesPerSymbol / ( 4.0 * alpha * index * index ) ); - denominator = -32.0 * Math.PI * alpha * alpha * index / - (double)samplesPerSymbol; + denominator = (float)( -32.0 * Math.PI * alpha * alpha * index / + (double)samplesPerSymbol ); } - coefficients[ x ] = 4.0 * alpha * numerator / denominator; + coefficients[ x ] = 4.0f * alpha * numerator / denominator; scale += coefficients[ x ]; } @@ -738,13 +737,4 @@ public class FilterFactory return coefficients; } - - public static void main( String[] args ) - { - double[] c = FilterFactory.getRootRaisedCosine( 10, 4, 0.2 ); - - mLog.debug( "C:" + Arrays.toString( c ) ); - } - - } diff --git a/src/dsp/filter/Filters.java b/src/dsp/filter/Filters.java index ab881c89..0381e5ea 100644 --- a/src/dsp/filter/Filters.java +++ b/src/dsp/filter/Filters.java @@ -4,189 +4,662 @@ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or + * the Free Software Foundationf, either version 3 of the Licensef, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be usefulf, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see + * along with this program. If notf, see ******************************************************************************/ package dsp.filter; public enum Filters { - FIRHP_LTR_DCBLOCK_750FS_50FC( new double[] + /** + * Designed with t-filter: + * + * 48000 Fsf, 243 taps + * 0 - 200 Hz: gain=0f, -60 db attenuation + * 500 - 3500 Hz: gain=1f, 5 db ripple + * 3800 - 24000 Hz: gain=0f, -60 db attenuation + */ + FIR_BANDPASS_AUDIO_48KHZ( new float[] { - 0.0679294122464217, - -0.07324095797199973, - -0.03838823381974882, - -0.013430897335300878, - 0.006449207638138327, - 0.022750490924149575, - 0.034175688012529584, - 0.037600969376502945, - 0.030002787357015913, - 0.009961076769098255, - -0.02214359076495753, - -0.062375656373163425, - -0.10487871348895818, - -0.14241968373731975, - -0.16818470634383087, - 0.8226518956829582, - -0.16818470634383087, - -0.14241968373731975, - -0.10487871348895818, - -0.062375656373163425, - -0.02214359076495753, - 0.009961076769098255, - 0.030002787357015913, - 0.037600969376502945, - 0.034175688012529584, - 0.022750490924149575, - 0.006449207638138327, - -0.013430897335300878, - -0.03838823381974882, - -0.07324095797199973, - 0.0679294122464217 + 0.0011306590506720503f, + 0.0017695582496011488f, + 0.0029288606290379564f, + 0.004343691928519147f, + 0.005911947383484083f, + 0.007475952925152455f, + 0.008834973531280245f, + 0.009767639642827303f, + 0.010062783359359985f, + 0.009554323352836855f, + 0.008154040477083785f, + 0.005876856248820146f, + 0.002853293692694345f, + -0.0006754620931569513f, + -0.004380476393750235f, + -0.007883982012534343f, + -0.010810030687940836f, + -0.01283857015700313f, + -0.013754750291231674f, + -0.013485225355167158f, + -0.012113350628392553f, + -0.009870878494518202f, + -0.007105858136673649f, + -0.00423044627271302f, + -0.0016581171943216698f, + 0.00026139558898877413f, + 0.0012960093376189904f, + 0.001367153398534282f, + 0.0005589404072224782f, + -0.0008998262476139348f, + -0.002678680933513012f, + -0.004405628120562205f, + -0.005733894494038466f, + -0.006403098522792899f, + -0.0062840458452382f, + -0.005398740409728341f, + -0.003912212267810966f, + -0.0020979501642753354f, + -0.0002835213596901672f, + 0.0012129422773069776f, + 0.002143441512259471f, + 0.0023762621098547794f, + 0.0019187818063353935f, + 0.000912904484557303f, + -0.0003959227311914178f, + -0.0017094242365984f, + -0.0027368218536981716f, + -0.0032529110615098964f, + -0.003141865590241452f, + -0.0024186267939756956f, + -0.0012232782401918676f, + 0.00020964495297755235f, + 0.0016007876062091302f, + 0.0026840078948525774f, + 0.003261066093523398f, + 0.0032416075292524913f, + 0.0026604426307957627f, + 0.0016685677475029945f, + 0.0005002180750227251f, + -0.0005771658431263752f, + -0.001319472490248709f, + -0.0015570243575186143f, + -0.001229709577267077f, + -0.00039860814610671267f, + 0.000768061742498229f, + 0.0020316064815484648f, + 0.0031342758646814173f, + 0.00385441982862473f, + 0.004053691192616693f, + 0.003706243244244987f, + 0.0029036666957800066f, + 0.0018348347602710623f, + 0.0007447703354245662f, + -0.00011835819081745851f, + -0.000556901991979898f, + -0.0004664033507391891f, + 0.00014190043902796031f, + 0.001143626440427841f, + 0.0023282929683617563f, + 0.0034451626872284316f, + 0.004257806518147909f, + 0.004595703675845672f, + 0.00439150448243702f, + 0.0036959141111179176f, + 0.0026669294496819663f, + 0.00153556329292172f, + 0.0005555624481582027f, + -0.000052181722788229754f, + -0.00014822483506696354f, + 0.0002944283222452884f, + 0.0011838275788186557f, + 0.0023285887949390117f, + 0.003479479143540588f, + 0.004383876725492502f, + 0.004841511744105689f, + 0.004748927884234896f, + 0.0041230326070274305f, + 0.003098290748206268f, + 0.0018980051891599843f, + 0.0007858270723442799f, + 0.000007808226096047636f, + -0.00026221415675647226f, + 0.000038194038926661374f, + 0.0008450198034699045f, + 0.001980683488085903f, + 0.0031920946811098026f, + 0.004205885473834353f, + 0.004788786880449399f, + 0.004799721085068433f, + 0.004222198149342628f, + 0.0031694947167750084f, + 0.0018613072553364282f, + 0.0005764055253590191f, + -0.00040837636254631156f, + -0.0008797864800588528f, + -0.0007362748788250025f, + -0.000012335427115590454f, + 0.001127487196311246f, + 0.0024230493118988245f, + 0.0035750267398429276f, + 0.004310745684369589f, + 0.004445118905106224f, + 0.003923338081120285f, + 0.0028351651513023415f, + 0.0013974479410391886f, + -0.00009204763249292721f, + -0.0013196387156487223f, + -0.0020251151156756176f, + -0.0020605167563326203f, + -0.001424951849329983f, + -0.00026755861117644484f, + 0.0011426434337099476f, + 0.00247453898902279f, + 0.0034075979415458474f, + 0.0037034647048603147f, + 0.003260419292510863f, + 0.002138889696298502f, + 0.0005507292550901969f, + -0.0011837011788563476f, + -0.002705803369109791f, + -0.0036965273158039683f, + -0.003950008183602666f, + -0.0034202362142058866f, + -0.002235167107431455f, + -0.0006724174233870484f, + 0.0008995473194704798f, + 0.0021008342984556796f, + 0.0026253485580778205f, + 0.002310873178862991f, + 0.0011772529466448514f, + -0.0005730908358460114f, + -0.002594671172069867f, + -0.0044734850087020245f, + -0.005818409323341712f, + -0.006347432664306618f, + -0.005952913139489849f, + -0.004732154819606038f, + -0.0029702110984690744f, + -0.0010791651288411521f, + 0.00048707995367597095f, + 0.0013335442378021411f, + 0.0012177475494058294f, + 0.00010122252104995588f, + -0.0018287043085791768f, + -0.004200030123435349f, + -0.006529365603823069f, + -0.00832894491185446f, + -0.009214117900210558f, + -0.008992727392755416f, + -0.007713940553513187f, + -0.005665102752109283f, + -0.0033154814998344715f, + -0.001217542957348092f, + 0.00011370751541866088f, + 0.00031672583066655f, + -0.0007305199337543714f, + -0.002874449900177386f, + -0.005710232857479199f, + -0.008662772551611736f, + -0.01110852438766387f, + -0.01251284869187753f, + -0.012552561484583724f, + -0.011196629466395516f, + -0.008725049876131727f, + -0.005679263313240144f, + -0.0027527053850089833f, + -0.0006423534393274008f, + 0.00010613666306157791f, + -0.0007726767835356292f, + -0.0031920086018739423f, + -0.006715187307484388f, + -0.010635986592925665f, + -0.014122526644297223f, + -0.016396230121947006f, + -0.016907881072255753f, + -0.015472266669959437f, + -0.01232951904769663f, + -0.008115919582767236f, + -0.0037455665411447506f, + -0.00022384102223090357f, + 0.0015702170495901357f, + 0.0010871601110870473f, + -0.0017492942655052172f, + -0.006493113668388964f, + -0.012237738308695591f, + -0.017782884779579476f, + -0.021877188332801504f, + -0.02348982727475883f, + -0.022055126496704366f, + -0.017635878927117044f, + -0.010965170848193264f, + -0.0033482781673564166f, + 0.003565343786600591f, + 0.008103372983477226f, + 0.008926637620116478f, + 0.005350637848922081f, + -0.002434639433805644f, + -0.013297914897997802f, + -0.025277752058596798f, + -0.03586329253291671f, + -0.042403748965243175f, + -0.04257882777767779f, + -0.034847147989454305f, + -0.018789707031704826f, + 0.004719508132752991f, + 0.033554756785942955f, + 0.06459420543249415f, + 0.09415420005424562f, + 0.11853369816477846f, + 0.13457931451963423f, + 0.14017594101985517f, + 0.13457931451963423f, + 0.11853369816477848f, + 0.09415420005424562f, + 0.06459420543249415f, + 0.033554756785942955f, + 0.004719508132752991f, + -0.018789707031704822f, + -0.034847147989454305f, + -0.04257882777767779f, + -0.042403748965243175f, + -0.03586329253291671f, + -0.025277752058596798f, + -0.01329791489799781f, + -0.002434639433805644f, + 0.005350637848922081f, + 0.008926637620116478f, + 0.008103372983477226f, + 0.003565343786600591f, + -0.0033482781673564166f, + -0.01096517084819326f, + -0.017635878927117044f, + -0.022055126496704366f, + -0.02348982727475883f, + -0.021877188332801504f, + -0.017782884779579476f, + -0.012237738308695591f, + -0.006493113668388964f, + -0.0017492942655052172f, + 0.0010871601110870439f, + 0.0015702170495901396f, + -0.00022384102223090309f, + -0.0037455665411447506f, + -0.00811591958276724f, + -0.01232951904769663f, + -0.015472266669959437f, + -0.016907881072255753f, + -0.016396230121947006f, + -0.014122526644297223f, + -0.010635986592925665f, + -0.006715187307484388f, + -0.0031920086018739444f, + -0.0007726767835356292f, + 0.0001061366630615779f, + -0.0006423534393274008f, + -0.0027527053850089833f, + -0.005679263313240145f, + -0.00872504987613173f, + -0.011196629466395517f, + -0.012552561484583724f, + -0.01251284869187753f, + -0.011108524387663866f, + -0.008662772551611736f, + -0.005710232857479197f, + -0.002874449900177386f, + -0.0007305199337543743f, + 0.00031672583066655f, + 0.0001137075154186604f, + -0.001217542957348092f, + -0.0033154814998344715f, + -0.005665102752109283f, + -0.007713940553513191f, + -0.008992727392755416f, + -0.009214117900210558f, + -0.008328944911854458f, + -0.006529365603823069f, + -0.004200030123435349f, + -0.0018287043085791778f, + 0.00010122252104995396f, + 0.0012177475494058294f, + 0.0013335442378021411f, + 0.00048707995367597095f, + -0.0010791651288411521f, + -0.002970211098469072f, + -0.004732154819606038f, + -0.005952913139489849f, + -0.0063474326643066185f, + -0.005818409323341712f, + -0.004473485008702026f, + -0.002594671172069867f, + -0.0005730908358460094f, + 0.0011772529466448514f, + 0.002310873178862991f, + 0.002625348558077821f, + 0.0021008342984556796f, + 0.0008995473194704788f, + -0.0006724174233870484f, + -0.002235167107431454f, + -0.0034202362142058866f, + -0.003950008183602666f, + -0.0036965273158039683f, + -0.002705803369109791f, + -0.0011837011788563506f, + 0.0005507292550901969f, + 0.002138889696298502f, + 0.003260419292510862f, + 0.0037034647048603147f, + 0.0034075979415458474f, + 0.00247453898902279f, + 0.0011426434337099476f, + -0.00026755861117644543f, + -0.0014249518493299838f, + -0.0020605167563326203f, + -0.0020251151156756176f, + -0.001319638715648723f, + -0.00009204763249292625f, + 0.0013974479410391886f, + 0.0028351651513023415f, + 0.003923338081120284f, + 0.004445118905106224f, + 0.00431074568436959f, + 0.0035750267398429276f, + 0.0024230493118988228f, + 0.001127487196311246f, + -0.000012335427115590454f, + -0.0007362748788250025f, + -0.0008797864800588528f, + -0.0004083763625463096f, + 0.000576405525359021f, + 0.0018613072553364282f, + 0.0031694947167750084f, + 0.0042221981493426285f, + 0.004799721085068431f, + 0.004788786880449399f, + 0.004205885473834353f, + 0.0031920946811098017f, + 0.001980683488085902f, + 0.0008450198034699026f, + 0.00003819403892666041f, + -0.0002622141567564703f, + 0.000007808226096045708f, + 0.0007858270723442809f, + 0.001898005189159986f, + 0.003098290748206268f, + 0.004123032607027432f, + 0.004748927884234897f, + 0.004841511744105689f, + 0.004383876725492502f, + 0.003479479143540588f, + 0.0023285887949390117f, + 0.0011838275788186557f, + 0.00029442832224528743f, + -0.00014822483506696354f, + -0.000052181722788230235f, + 0.0005555624481582017f, + 0.0015355632929217181f, + 0.002666929449681966f, + 0.0036959141111179176f, + 0.004391504482437019f, + 0.004595703675845674f, + 0.004257806518147909f, + 0.0034451626872284316f, + 0.002328292968361755f, + 0.0011436264404278407f, + 0.0001419004390279603f, + -0.00046640335073919057f, + -0.000556901991979898f, + -0.00011835819081745802f, + 0.0007447703354245662f, + 0.0018348347602710623f, + 0.0029036666957800066f, + 0.0037062432442449863f, + 0.0040536911926166925f, + 0.003854419828624731f, + 0.0031342758646814165f, + 0.0020316064815484656f, + 0.00076806174249823f, + -0.00039860814610671267f, + -0.001229709577267077f, + -0.0015570243575186143f, + -0.001319472490248709f, + -0.0005771658431263752f, + 0.000500218075022725f, + 0.001668567747502994f, + 0.002660442630795762f, + 0.0032416075292524913f, + 0.0032610660935233986f, + 0.002684007894852579f, + 0.0016007876062091293f, + 0.00020964495297755235f, + -0.0012232782401918676f, + -0.002418626793975698f, + -0.003141865590241452f, + -0.003252911061509897f, + -0.002736821853698173f, + -0.0017094242365984003f, + -0.0003959227311914187f, + 0.0009129044845573019f, + 0.0019187818063353941f, + 0.0023762621098547807f, + 0.00214344151225947f, + 0.0012129422773069776f, + -0.0002835213596901672f, + -0.0020979501642753354f, + -0.003912212267810966f, + -0.005398740409728341f, + -0.0062840458452382f, + -0.006403098522792899f, + -0.005733894494038467f, + -0.004405628120562203f, + -0.002678680933513013f, + -0.0008998262476139358f, + 0.0005589404072224788f, + 0.001367153398534282f, + 0.0012960093376189895f, + 0.0002613955889887717f, + -0.0016581171943216698f, + -0.00423044627271302f, + -0.007105858136673649f, + -0.009870878494518204f, + -0.012113350628392553f, + -0.013485225355167158f, + -0.01375475029123167f, + -0.01283857015700313f, + -0.010810030687940836f, + -0.007883982012534343f, + -0.004380476393750235f, + -0.0006754620931569513f, + 0.0028532936926943457f, + 0.005876856248820146f, + 0.008154040477083787f, + 0.009554323352836855f, + 0.010062783359359985f, + 0.009767639642827303f, + 0.008834973531280245f, + 0.007475952925152455f, + 0.005911947383484083f, + 0.004343691928519149f, + 0.0029288606290379547f, + 0.00176955824960115f, + 0.0011306590506720503f + } ), + + FIRHP_LTR_DCBLOCK_750FS_50FC( new float[] + { + 0.0679294122464217f, + -0.07324095797199973f, + -0.03838823381974882f, + -0.013430897335300878f, + 0.006449207638138327f, + 0.022750490924149575f, + 0.034175688012529584f, + 0.037600969376502945f, + 0.030002787357015913f, + 0.009961076769098255f, + -0.02214359076495753f, + -0.062375656373163425f, + -0.10487871348895818f, + -0.14241968373731975f, + -0.16818470634383087f, + 0.8226518956829582f, + -0.16818470634383087f, + -0.14241968373731975f, + -0.10487871348895818f, + -0.062375656373163425f, + -0.02214359076495753f, + 0.009961076769098255f, + 0.030002787357015913f, + 0.037600969376502945f, + 0.034175688012529584f, + 0.022750490924149575f, + 0.006449207638138327f, + -0.013430897335300878f, + -0.03838823381974882f, + -0.07324095797199973f, + 0.0679294122464217f }), - FIRLP_LTR_1500FS( new double[] + FIRLP_LTR_1500FS( new float[] { - -0.008272676875607624, - -0.0024939020178146617, - 0.010376092557732189, - 0.011962525412519345, - -0.003977368587769054, - -0.013289773851060555, - -0.0029497606301098858, - 0.003941811083306652, - -0.003154954069081114, - 0.005485070321679545, - 0.03171909301543344, - 0.015387284536986508, - -0.055467824110903244, - -0.07246950346172203, - 0.03466378905394898, - 0.1322297692443333, - 0.04683098737206109, - -0.13469992806577527, - -0.14450919452286415, - 0.0578348928408703, - 0.18926196545202326, - 0.0578348928408703, - -0.14450919452286415, - -0.13469992806577527, - 0.04683098737206109, - 0.1322297692443333, - 0.03466378905394898, - -0.07246950346172203, - -0.055467824110903244, - 0.015387284536986508, - 0.03171909301543344, - 0.005485070321679545, - -0.003154954069081114, - 0.003941811083306652, - -0.0029497606301098858, - -0.013289773851060555, - -0.003977368587769054, - 0.011962525412519345, - 0.010376092557732189, - -0.0024939020178146617, - -0.008272676875607624 + -0.008272676875607624f, + -0.0024939020178146617f, + 0.010376092557732189f, + 0.011962525412519345f, + -0.003977368587769054f, + -0.013289773851060555f, + -0.0029497606301098858f, + 0.003941811083306652f, + -0.003154954069081114f, + 0.005485070321679545f, + 0.03171909301543344f, + 0.015387284536986508f, + -0.055467824110903244f, + -0.07246950346172203f, + 0.03466378905394898f, + 0.1322297692443333f, + 0.04683098737206109f, + -0.13469992806577527f, + -0.14450919452286415f, + 0.0578348928408703f, + 0.18926196545202326f, + 0.0578348928408703f, + -0.14450919452286415f, + -0.13469992806577527f, + 0.04683098737206109f, + 0.1322297692443333f, + 0.03466378905394898f, + -0.07246950346172203f, + -0.055467824110903244f, + 0.015387284536986508f, + 0.03171909301543344f, + 0.005485070321679545f, + -0.003154954069081114f, + 0.003941811083306652f, + -0.0029497606301098858f, + -0.013289773851060555f, + -0.003977368587769054f, + 0.011962525412519345f, + 0.010376092557732189f, + -0.0024939020178146617f, + -0.008272676875607624f }), - FIRBP_1200FSK_24000FS( new double[] + FIRBP_1200FSK_24000FS( new float[] { - 0.004792788663425844, - 0.004034377807422182, - 0.004699808237099943, - 0.00443210843891432, - 0.0028638644082473, - -0.00015292185838515035, - -0.004394278846594682, - -0.009212560275995686, - -0.013596659741332163, - -0.016383584186535002, - -0.016573550574018152, - -0.013657566836436204, - -0.007851047182221552, - -0.00014425394402752814, - 0.007885453072038891, - 0.014465082677564993, - 0.01811703090261803, - 0.01810997536219774, - 0.014727244087571567, - 0.009244939917342813, - 0.0036035605020527947, - -0.00014781830127321208, - -0.0004913597198315713, - 0.0030136991548897693, - 0.009453462203915387, - 0.01666055505335309, - 0.02170180561798826, - 0.021656701887523472, - 0.014498683513932197, - -0.0001565221289617459, - -0.02065343799220559, - -0.04339467795574746, - -0.06347784859804807, - -0.07576298936541592, - -0.07612281593093975, - -0.06256712023008996, - -0.03596397841747506, - -0.00012170810308311971, - 0.03885147059625151, - 0.07377979316631962, - 0.09792980519752477, - 0.10655596332800969, - 0.09792980519752477, - 0.07377979316631962, - 0.03885147059625151, - -0.00012170810308311971, - -0.03596397841747506, - -0.06256712023008996, - -0.07612281593093975, - -0.07576298936541592, - -0.06347784859804807, - -0.04339467795574746, - -0.02065343799220559, - -0.0001565221289617459, - 0.014498683513932197, - 0.021656701887523472, - 0.02170180561798826, - 0.01666055505335309, - 0.009453462203915387, - 0.0030136991548897693, - -0.0004913597198315713, - -0.00014781830127321208, - 0.0036035605020527947, - 0.009244939917342813, - 0.014727244087571567, - 0.01810997536219774, - 0.01811703090261803, - 0.014465082677564993, - 0.007885453072038891, - -0.00014425394402752814, - -0.007851047182221552, - -0.013657566836436204, - -0.016573550574018152, - -0.016383584186535002, - -0.013596659741332163, - -0.009212560275995686, - -0.004394278846594682, - -0.00015292185838515035, - 0.0028638644082473, - 0.00443210843891432, - 0.004699808237099943, - 0.004034377807422182, - 0.004792788663425844 + 0.004792788663425844f, + 0.004034377807422182f, + 0.004699808237099943f, + 0.00443210843891432f, + 0.0028638644082473f, + -0.00015292185838515035f, + -0.004394278846594682f, + -0.009212560275995686f, + -0.013596659741332163f, + -0.016383584186535002f, + -0.016573550574018152f, + -0.013657566836436204f, + -0.007851047182221552f, + -0.00014425394402752814f, + 0.007885453072038891f, + 0.014465082677564993f, + 0.01811703090261803f, + 0.01810997536219774f, + 0.014727244087571567f, + 0.009244939917342813f, + 0.0036035605020527947f, + -0.00014781830127321208f, + -0.0004913597198315713f, + 0.0030136991548897693f, + 0.009453462203915387f, + 0.01666055505335309f, + 0.02170180561798826f, + 0.021656701887523472f, + 0.014498683513932197f, + -0.0001565221289617459f, + -0.02065343799220559f, + -0.04339467795574746f, + -0.06347784859804807f, + -0.07576298936541592f, + -0.07612281593093975f, + -0.06256712023008996f, + -0.03596397841747506f, + -0.00012170810308311971f, + 0.03885147059625151f, + 0.07377979316631962f, + 0.09792980519752477f, + 0.10655596332800969f, + 0.09792980519752477f, + 0.07377979316631962f, + 0.03885147059625151f, + -0.00012170810308311971f, + -0.03596397841747506f, + -0.06256712023008996f, + -0.07612281593093975f, + -0.07576298936541592f, + -0.06347784859804807f, + -0.04339467795574746f, + -0.02065343799220559f, + -0.0001565221289617459f, + 0.014498683513932197f, + 0.021656701887523472f, + 0.02170180561798826f, + 0.01666055505335309f, + 0.009453462203915387f, + 0.0030136991548897693f, + -0.0004913597198315713f, + -0.00014781830127321208f, + 0.0036035605020527947f, + 0.009244939917342813f, + 0.014727244087571567f, + 0.01810997536219774f, + 0.01811703090261803f, + 0.014465082677564993f, + 0.007885453072038891f, + -0.00014425394402752814f, + -0.007851047182221552f, + -0.013657566836436204f, + -0.016573550574018152f, + -0.016383584186535002f, + -0.013596659741332163f, + -0.009212560275995686f, + -0.004394278846594682f, + -0.00015292185838515035f, + 0.0028638644082473f, + 0.00443210843891432f, + 0.004699808237099943f, + 0.004034377807422182f, + 0.004792788663425844f }), - FIRLP_3K( new double[] + FIRLP_3K( new float[] { /* @@ -207,112 +680,112 @@ public enum Filters */ - -0.00011443124614437417, - -0.00017293152659867192, - -0.0002537395679787903, - -0.0002960101735353313, - -0.00023955416024142116, - -0.000005428557963321982, - 0.0004964100334261843, - 0.0013538842757409063, - 0.002634918910611058, - 0.00436693575408337, - 0.0065175161697321555, - 0.00897952475800971, - 0.011566379161822652, - 0.014018876408451886, - 0.016028053953955006, - 0.017270887068042688, - 0.01745751423179922, - 0.0163833187291431, - 0.013977100662194252, - 0.010337169842377791, - 0.005746282323474722, - 0.0006589019184637925, - -0.004341600521758248, - -0.008613131831428159, - -0.011545791615276019, - -0.012660543617859996, - -0.011700549440186059, - -0.008698639621450092, - -0.004006684845818583, - 0.00172317360274727, - 0.007608807120062409, - 0.012654244324839246, - 0.015898391911284164, - 0.016575938784747404, - 0.014265883702010186, - 0.009002241493722784, - 0.0013255862247370318, - -0.007739007276591945, - -0.0167810834076902, - -0.024172675639705345, - -0.02828537119270343, - -0.027727506101182933, - -0.02156733481426441, - -0.009507700677949931, - 0.008016566866990643, - 0.02983764491633055, - 0.05415546347475946, - 0.0787307236559999, - 0.10114644750135711, - 0.11910360136213424, - 0.13071022495045617, - 0.13472344727490876, - 0.13071022495045617, - 0.11910360136213424, - 0.10114644750135711, - 0.0787307236559999, - 0.05415546347475946, - 0.02983764491633055, - 0.008016566866990643, - -0.009507700677949931, - -0.02156733481426441, - -0.027727506101182933, - -0.02828537119270343, - -0.024172675639705345, - -0.0167810834076902, - -0.007739007276591945, - 0.0013255862247370318, - 0.009002241493722784, - 0.014265883702010186, - 0.016575938784747404, - 0.015898391911284164, - 0.012654244324839246, - 0.007608807120062409, - 0.00172317360274727, - -0.004006684845818583, - -0.008698639621450092, - -0.011700549440186059, - -0.012660543617859996, - -0.011545791615276019, - -0.008613131831428159, - -0.004341600521758248, - 0.0006589019184637925, - 0.005746282323474722, - 0.010337169842377791, - 0.013977100662194252, - 0.0163833187291431, - 0.01745751423179922, - 0.017270887068042688, - 0.016028053953955006, - 0.014018876408451886, - 0.011566379161822652, - 0.00897952475800971, - 0.0065175161697321555, - 0.00436693575408337, - 0.002634918910611058, - 0.0013538842757409063, - 0.0004964100334261843, - -0.000005428557963321982, - -0.00023955416024142116, - -0.0002960101735353313, - -0.0002537395679787903, - -0.00017293152659867192, - -0.00011443124614437417 + -0.00011443124614437417f, + -0.00017293152659867192f, + -0.0002537395679787903f, + -0.0002960101735353313f, + -0.00023955416024142116f, + -0.000005428557963321982f, + 0.0004964100334261843f, + 0.0013538842757409063f, + 0.002634918910611058f, + 0.00436693575408337f, + 0.0065175161697321555f, + 0.00897952475800971f, + 0.011566379161822652f, + 0.014018876408451886f, + 0.016028053953955006f, + 0.017270887068042688f, + 0.01745751423179922f, + 0.0163833187291431f, + 0.013977100662194252f, + 0.010337169842377791f, + 0.005746282323474722f, + 0.0006589019184637925f, + -0.004341600521758248f, + -0.008613131831428159f, + -0.011545791615276019f, + -0.012660543617859996f, + -0.011700549440186059f, + -0.008698639621450092f, + -0.004006684845818583f, + 0.00172317360274727f, + 0.007608807120062409f, + 0.012654244324839246f, + 0.015898391911284164f, + 0.016575938784747404f, + 0.014265883702010186f, + 0.009002241493722784f, + 0.0013255862247370318f, + -0.007739007276591945f, + -0.0167810834076902f, + -0.024172675639705345f, + -0.02828537119270343f, + -0.027727506101182933f, + -0.02156733481426441f, + -0.009507700677949931f, + 0.008016566866990643f, + 0.02983764491633055f, + 0.05415546347475946f, + 0.0787307236559999f, + 0.10114644750135711f, + 0.11910360136213424f, + 0.13071022495045617f, + 0.13472344727490876f, + 0.13071022495045617f, + 0.11910360136213424f, + 0.10114644750135711f, + 0.0787307236559999f, + 0.05415546347475946f, + 0.02983764491633055f, + 0.008016566866990643f, + -0.009507700677949931f, + -0.02156733481426441f, + -0.027727506101182933f, + -0.02828537119270343f, + -0.024172675639705345f, + -0.0167810834076902f, + -0.007739007276591945f, + 0.0013255862247370318f, + 0.009002241493722784f, + 0.014265883702010186f, + 0.016575938784747404f, + 0.015898391911284164f, + 0.012654244324839246f, + 0.007608807120062409f, + 0.00172317360274727f, + -0.004006684845818583f, + -0.008698639621450092f, + -0.011700549440186059f, + -0.012660543617859996f, + -0.011545791615276019f, + -0.008613131831428159f, + -0.004341600521758248f, + 0.0006589019184637925f, + 0.005746282323474722f, + 0.010337169842377791f, + 0.013977100662194252f, + 0.0163833187291431f, + 0.01745751423179922f, + 0.017270887068042688f, + 0.016028053953955006f, + 0.014018876408451886f, + 0.011566379161822652f, + 0.00897952475800971f, + 0.0065175161697321555f, + 0.00436693575408337f, + 0.002634918910611058f, + 0.0013538842757409063f, + 0.0004964100334261843f, + -0.000005428557963321982f, + -0.00023955416024142116f, + -0.0002960101735353313f, + -0.0002537395679787903f, + -0.00017293152659867192f, + -0.00011443124614437417f } ), - FIRLP_8K( new double[] { + FIRLP_8K( new float[] { /* @@ -333,132 +806,132 @@ public enum Filters */ - -0.0005876043141107231, - 0.0000395798795893033, - 0.0007936108691418746, - 0.002271220583758663, - 0.0045690708238380244, - 0.007632833240537176, - 0.011210445195711924, - 0.014848077882921171, - 0.01794155760934873, - 0.019840991376714804, - 0.01999155424676768, - 0.018081523929298803, - 0.01415831726867514, - 0.008676730505642374, - 0.0024562153938831365, - -0.0034571527303437705, - -0.008001879410852286, - -0.010340843984508226, - -0.010072395969237698, - -0.007354895192546896, - -0.0029039203557233197, - 0.0021492484218134564, - 0.006514352308475102, - 0.009056570176096615, - 0.009090754245710363, - 0.006576743943510026, - 0.002156484338606342, - -0.0029881335341335434, - -0.007431040256860951, - -0.009881347307264159, - -0.009547761214420764, - -0.006387716720906473, - -0.00116384277476187, - 0.004722257199180802, - 0.0095865822102477, - 0.011924484237795421, - 0.010856600363787754, - 0.006429626923814576, - -0.00032219256325637487, - -0.007591488679896054, - -0.01325346967699737, - -0.015451428687349951, - -0.013159262174429455, - -0.006567549275313882, - 0.0028476895325932673, - 0.012598008810195174, - 0.019781804328972176, - 0.02186025227953089, - 0.017422058628364847, - 0.006738234527549823, - -0.008066547704296321, - -0.023306710233840622, - -0.0344567035523893, - -0.037158269971142174, - -0.028289474602001076, - -0.006838858354697991, - 0.02563946671507246, - 0.06512400470969, - 0.10578445793560229, - 0.141076536562424, - 0.16504202130157974, - 0.17352664849913732, - 0.16504202130157974, - 0.141076536562424, - 0.10578445793560229, - 0.06512400470969, - 0.02563946671507246, - -0.006838858354697991, - -0.028289474602001076, - -0.037158269971142174, - -0.0344567035523893, - -0.023306710233840622, - -0.008066547704296321, - 0.006738234527549823, - 0.017422058628364847, - 0.02186025227953089, - 0.019781804328972176, - 0.012598008810195174, - 0.0028476895325932673, - -0.006567549275313882, - -0.013159262174429455, - -0.015451428687349951, - -0.01325346967699737, - -0.007591488679896054, - -0.00032219256325637487, - 0.006429626923814576, - 0.010856600363787754, - 0.011924484237795421, - 0.0095865822102477, - 0.004722257199180802, - -0.00116384277476187, - -0.006387716720906473, - -0.009547761214420764, - -0.009881347307264159, - -0.007431040256860951, - -0.0029881335341335434, - 0.002156484338606342, - 0.006576743943510026, - 0.009090754245710363, - 0.009056570176096615, - 0.006514352308475102, - 0.0021492484218134564, - -0.0029039203557233197, - -0.007354895192546896, - -0.010072395969237698, - -0.010340843984508226, - -0.008001879410852286, - -0.0034571527303437705, - 0.0024562153938831365, - 0.008676730505642374, - 0.01415831726867514, - 0.018081523929298803, - 0.01999155424676768, - 0.019840991376714804, - 0.01794155760934873, - 0.014848077882921171, - 0.011210445195711924, - 0.007632833240537176, - 0.0045690708238380244, - 0.002271220583758663, - 0.0007936108691418746, - 0.0000395798795893033, - -0.0005876043141107231 + -0.0005876043141107231f, + 0.0000395798795893033f, + 0.0007936108691418746f, + 0.002271220583758663f, + 0.0045690708238380244f, + 0.007632833240537176f, + 0.011210445195711924f, + 0.014848077882921171f, + 0.01794155760934873f, + 0.019840991376714804f, + 0.01999155424676768f, + 0.018081523929298803f, + 0.01415831726867514f, + 0.008676730505642374f, + 0.0024562153938831365f, + -0.0034571527303437705f, + -0.008001879410852286f, + -0.010340843984508226f, + -0.010072395969237698f, + -0.007354895192546896f, + -0.0029039203557233197f, + 0.0021492484218134564f, + 0.006514352308475102f, + 0.009056570176096615f, + 0.009090754245710363f, + 0.006576743943510026f, + 0.002156484338606342f, + -0.0029881335341335434f, + -0.007431040256860951f, + -0.009881347307264159f, + -0.009547761214420764f, + -0.006387716720906473f, + -0.00116384277476187f, + 0.004722257199180802f, + 0.0095865822102477f, + 0.011924484237795421f, + 0.010856600363787754f, + 0.006429626923814576f, + -0.00032219256325637487f, + -0.007591488679896054f, + -0.01325346967699737f, + -0.015451428687349951f, + -0.013159262174429455f, + -0.006567549275313882f, + 0.0028476895325932673f, + 0.012598008810195174f, + 0.019781804328972176f, + 0.02186025227953089f, + 0.017422058628364847f, + 0.006738234527549823f, + -0.008066547704296321f, + -0.023306710233840622f, + -0.0344567035523893f, + -0.037158269971142174f, + -0.028289474602001076f, + -0.006838858354697991f, + 0.02563946671507246f, + 0.06512400470969f, + 0.10578445793560229f, + 0.141076536562424f, + 0.16504202130157974f, + 0.17352664849913732f, + 0.16504202130157974f, + 0.141076536562424f, + 0.10578445793560229f, + 0.06512400470969f, + 0.02563946671507246f, + -0.006838858354697991f, + -0.028289474602001076f, + -0.037158269971142174f, + -0.0344567035523893f, + -0.023306710233840622f, + -0.008066547704296321f, + 0.006738234527549823f, + 0.017422058628364847f, + 0.02186025227953089f, + 0.019781804328972176f, + 0.012598008810195174f, + 0.0028476895325932673f, + -0.006567549275313882f, + -0.013159262174429455f, + -0.015451428687349951f, + -0.01325346967699737f, + -0.007591488679896054f, + -0.00032219256325637487f, + 0.006429626923814576f, + 0.010856600363787754f, + 0.011924484237795421f, + 0.0095865822102477f, + 0.004722257199180802f, + -0.00116384277476187f, + -0.006387716720906473f, + -0.009547761214420764f, + -0.009881347307264159f, + -0.007431040256860951f, + -0.0029881335341335434f, + 0.002156484338606342f, + 0.006576743943510026f, + 0.009090754245710363f, + 0.009056570176096615f, + 0.006514352308475102f, + 0.0021492484218134564f, + -0.0029039203557233197f, + -0.007354895192546896f, + -0.010072395969237698f, + -0.010340843984508226f, + -0.008001879410852286f, + -0.0034571527303437705f, + 0.0024562153938831365f, + 0.008676730505642374f, + 0.01415831726867514f, + 0.018081523929298803f, + 0.01999155424676768f, + 0.019840991376714804f, + 0.01794155760934873f, + 0.014848077882921171f, + 0.011210445195711924f, + 0.007632833240537176f, + 0.0045690708238380244f, + 0.002271220583758663f, + 0.0007936108691418746f, + 0.0000395798795893033f, + -0.0005876043141107231f } ), - FIRLP_15K( new double[] { + FIRLP_15K( new float[] { /* FIR filter designed with http://t-filter.appspot.com sampling frequency: 48000 Hz @@ -471,77 +944,77 @@ public enum Filters desired attenuation = -40 dB actual attenuation = -41.03342332843521 dB */ - -0.014082901225560598, - -0.02333969799770533, - -0.022033345340200808, - -0.0004492742003176776, - 0.034217272170844526, - 0.05847499164066433, - 0.04845184454729058, - 0.0029885635174003813, - -0.045972124010673326, - -0.04957432533335844, - 0.02225593631403953, - 0.15189293741405197, - 0.27718300156351594, - 0.32893125787628796, - 0.27718300156351594, - 0.15189293741405197, - 0.02225593631403953, - -0.04957432533335844, - -0.045972124010673326, - 0.0029885635174003813, - 0.04845184454729058, - 0.05847499164066433, - 0.034217272170844526, - -0.0004492742003176776, - -0.022033345340200808, - -0.02333969799770533, - -0.014082901225560598 + -0.014082901225560598f, + -0.02333969799770533f, + -0.022033345340200808f, + -0.0004492742003176776f, + 0.034217272170844526f, + 0.05847499164066433f, + 0.04845184454729058f, + 0.0029885635174003813f, + -0.045972124010673326f, + -0.04957432533335844f, + 0.02225593631403953f, + 0.15189293741405197f, + 0.27718300156351594f, + 0.32893125787628796f, + 0.27718300156351594f, + 0.15189293741405197f, + 0.02225593631403953f, + -0.04957432533335844f, + -0.045972124010673326f, + 0.0029885635174003813f, + 0.04845184454729058f, + 0.05847499164066433f, + 0.034217272170844526f, + -0.0004492742003176776f, + -0.022033345340200808f, + -0.02333969799770533f, + -0.014082901225560598f } ), //FS=192000 FC=40000 WIN: Kaiser-Bessel - FIR_HALF_BAND_31T_ONE_EIGHTH_FCO( new double[] { - -0.000045886616046836, 0, - 0.000591369980811269, 0, - -0.002865894676690263, 0, - 0.008258847333251895, 0, - -0.019555838964709603, 0, - 0.041885910942306370, 0, - -0.091494773870957058, 0, - 0.313146427083389300, 0.5, //.499981677577289670, - 0.313146427083389300, 0, - -0.091494773870957058, 0, - 0.041885910942306370, 0, - -0.019555838964709603, 0, - 0.008258847333251895, 0, - -0.002865894676690263, 0, - 0.000591369980811269, 0, - -0.000045886616046836 + FIR_HALF_BAND_31T_ONE_EIGHTH_FCO( new float[] { + -0.000045886616046836f, 0f, + 0.000591369980811269f, 0f, + -0.002865894676690263f, 0f, + 0.008258847333251895f, 0f, + -0.019555838964709603f, 0f, + 0.041885910942306370f, 0f, + -0.091494773870957058f, 0f, + 0.313146427083389300f, 0.5f, //.499981677577289670f, + 0.313146427083389300f, 0f, + -0.091494773870957058f, 0f, + 0.041885910942306370f, 0f, + -0.019555838964709603f, 0f, + 0.008258847333251895f, 0f, + -0.002865894676690263f, 0f, + 0.000591369980811269f, 0f, + -0.000045886616046836f } ), - FIRHP_31T_48KFS_300FCO( new double[] { 0.079454311268354116, - -0.431252134555296540, -0.080866558157993096, -0.018201087229858796, - -0.006959451230405745, -0.005082182047181806, -0.004827982359353442, - -0.004678622485731351, -0.004518783884113521, -0.004590986714193629, - -0.004788367734908081, -0.004864687584309190, -0.004847881778381928, - -0.004819115432733451, -0.004899065399109821, 0.995074461482826860, - -0.004899065399109821, -0.004819115432733451, -0.004847881778381928, - -0.004864687584309190, -0.004788367734908081, -0.004590986714193629, - -0.004518783884113521, -0.004678622485731351, -0.004827982359353442, - -0.005082182047181806, -0.006959451230405745, -0.018201087229858796, - -0.080866558157993096, -0.431252134555296540, 0.079454311268354116 } ), + FIRHP_31T_48KFS_300FCO( new float[] { 0.079454311268354116f, + -0.431252134555296540f, -0.080866558157993096f, -0.018201087229858796f, + -0.006959451230405745f, -0.005082182047181806f, -0.004827982359353442f, + -0.004678622485731351f, -0.004518783884113521f, -0.004590986714193629f, + -0.004788367734908081f, -0.004864687584309190f, -0.004847881778381928f, + -0.004819115432733451f, -0.004899065399109821f, 0.995074461482826860f, + -0.004899065399109821f, -0.004819115432733451f, -0.004847881778381928f, + -0.004864687584309190f, -0.004788367734908081f, -0.004590986714193629f, + -0.004518783884113521f, -0.004678622485731351f, -0.004827982359353442f, + -0.005082182047181806f, -0.006959451230405745f, -0.018201087229858796f, + -0.080866558157993096f, -0.431252134555296540f, 0.079454311268354116f } ), - FIRHP_31T_12KFS_400FCO( new double[] { 0.053389156955298771, - 0.182002884434506210, -0.086127791245869528, -0.016516534724731153, - -0.036968390772931715, -0.035475484228869217, -0.040734954194563675, - -0.044789081155425826, -0.049513180269900807, -0.054086500917566893, - -0.058563271660686450, -0.062449865988963169, -0.065441928590436960, - -0.068170371167388846, -0.069540820637351994, 0.930065488549365990, - -0.069540820637351994, -0.068170371167388846, -0.065441928590436960, - -0.062449865988963169, -0.058563271660686450, -0.054086500917566893, - -0.049513180269900807, -0.044789081155425826, -0.040734954194563675, - -0.035475484228869217, -0.036968390772931715, -0.016516534724731153, - -0.086127791245869528, 0.182002884434506210, 0.053389156955298771 } ), + FIRHP_31T_12KFS_400FCO( new float[] { 0.053389156955298771f, + 0.182002884434506210f, -0.086127791245869528f, -0.016516534724731153f, + -0.036968390772931715f, -0.035475484228869217f, -0.040734954194563675f, + -0.044789081155425826f, -0.049513180269900807f, -0.054086500917566893f, + -0.058563271660686450f, -0.062449865988963169f, -0.065441928590436960f, + -0.068170371167388846f, -0.069540820637351994f, 0.930065488549365990f, + -0.069540820637351994f, -0.068170371167388846f, -0.065441928590436960f, + -0.062449865988963169f, -0.058563271660686450f, -0.054086500917566893f, + -0.049513180269900807f, -0.044789081155425826f, -0.040734954194563675f, + -0.035475484228869217f, -0.036968390772931715f, -0.016516534724731153f, + -0.086127791245869528f, 0.182002884434506210f, 0.053389156955298771f } ), /** * FIR Low Pass Filter @@ -551,25 +1024,25 @@ public enum Filters * Designer: http://t-filter.appspot.com/fir/index.html */ - FIRLP_55TAP_48000FS_3000FC( new double[] { -0.0052419787903715655, - -0.0003789909443307852, 0.0012970137854134386, 0.004210460544067679, - 0.008221266221665309, 0.012951576579356689, 0.017794312853913318, - 0.021959871931790928, 0.024583800118549094, 0.024878144443784132, - 0.022296319647647538, 0.016685495012933472, 0.008390268267799136, - -0.001718264469387386, -0.012288389660616603, -0.02161633130497537, - -0.027882128567957774, -0.02943377233328899, -0.025006516193566884, - -0.014040074880106284, 0.0032217508351258103, 0.025660031586092195, - 0.05136009984280478, 0.0778336106888181, 0.10230926486919244, - 0.12209961841343948, 0.13496813439824185, 0.13943117253534673, - 0.13496813439824185, 0.12209961841343948, 0.10230926486919244, - 0.0778336106888181, 0.05136009984280478, 0.025660031586092195, - 0.0032217508351258103, -0.014040074880106284, -0.025006516193566884, - -0.02943377233328899, -0.027882128567957774, -0.02161633130497537, - -0.012288389660616603, -0.001718264469387386, 0.008390268267799136, - 0.016685495012933472, 0.022296319647647538, 0.024878144443784132, - 0.024583800118549094, 0.021959871931790928, 0.017794312853913318, - 0.012951576579356689, 0.008221266221665309, 0.004210460544067679, - 0.0012970137854134386, -0.0003789909443307852, -0.0052419787903715655 } ), + FIRLP_55TAP_48000FS_3000FC( new float[] { -0.0052419787903715655f, + -0.0003789909443307852f, 0.0012970137854134386f, 0.004210460544067679f, + 0.008221266221665309f, 0.012951576579356689f, 0.017794312853913318f, + 0.021959871931790928f, 0.024583800118549094f, 0.024878144443784132f, + 0.022296319647647538f, 0.016685495012933472f, 0.008390268267799136f, + -0.001718264469387386f, -0.012288389660616603f, -0.02161633130497537f, + -0.027882128567957774f, -0.02943377233328899f, -0.025006516193566884f, + -0.014040074880106284f, 0.0032217508351258103f, 0.025660031586092195f, + 0.05136009984280478f, 0.0778336106888181f, 0.10230926486919244f, + 0.12209961841343948f, 0.13496813439824185f, 0.13943117253534673f, + 0.13496813439824185f, 0.12209961841343948f, 0.10230926486919244f, + 0.0778336106888181f, 0.05136009984280478f, 0.025660031586092195f, + 0.0032217508351258103f, -0.014040074880106284f, -0.025006516193566884f, + -0.02943377233328899f, -0.027882128567957774f, -0.02161633130497537f, + -0.012288389660616603f, -0.001718264469387386f, 0.008390268267799136f, + 0.016685495012933472f, 0.022296319647647538f, 0.024878144443784132f, + 0.024583800118549094f, 0.021959871931790928f, 0.017794312853913318f, + 0.012951576579356689f, 0.008221266221665309f, 0.004210460544067679f, + 0.0012970137854134386f, -0.0003789909443307852f, -0.0052419787903715655f } ), /* @@ -589,7 +1062,7 @@ public enum Filters actual attenuation = -40.00189415137373 dB */ - FIRLP_24000FS_TO_3000FS( new double[] { + FIRLP_24000FS_TO_3000FS( new float[] { /* @@ -610,10 +1083,10 @@ public enum Filters */ - .2,.2,.2,.2,.2,.2,.2 + .2f,.2f,.2f,.2f,.2f,.2f,.2f }), - FIRLP_3000FS_TO_150FC( new double[] { + FIRLP_3000FS_TO_150FC( new float[] { /* FIR filter designed with @@ -633,605 +1106,605 @@ public enum Filters */ - -0.006122920352830965, - -0.0023884346335371165, - -0.002599310268302821, - -0.002619226598223891, - -0.0024058915455907196, - -0.0019288301578398123, - -0.0011732914244696805, - -0.00014348647168516363, - 0.0011360279713313209, - 0.002620018618160862, - 0.004244279668461443, - 0.005929041211240287, - 0.007583700132903604, - 0.009112816853143859, - 0.010422968037142797, - 0.011429759533610568, - 0.012064461696610605, - 0.01227985703081096, - 0.012054670302231996, - 0.011395973228693276, - 0.010339492321850014, - 0.008947826461137941, - 0.007306289027941143, - 0.005516680433668245, - 0.003689888284206567, - 0.0019376716631701368, - 0.00036406537300421297, - -0.0009423273945619157, - -0.0019147615431778597, - -0.0025137861299055595, - -0.00272885000805846, - -0.002579780077995427, - -0.00211370881796881, - -0.001401086778987431, - -0.0005294931107453994, - 0.00040506185948651675, - 0.0013056441129537509, - 0.0020838354221374305, - 0.0026667562654551246, - 0.003003052919640715, - 0.003066845756573675, - 0.002858826770125135, - 0.002405247908616018, - 0.0017550758096436146, - 0.0009750187684900944, - 0.0001428944628485573, - -0.0006600057708364918, - -0.0013572018806166464, - -0.0018849613428995964, - -0.0021983440926621103, - -0.002274533536918245, - -0.0021136819231921474, - -0.0017380160407736585, - -0.0011899674040168643, - -0.0005293364966829129, - 0.00017203581592664565, - 0.0008397052861045773, - 0.0014076463254305174, - 0.0018238726340835366, - 0.0020485384828633777, - 0.002054368304440125, - 0.00184518937028339, - 0.001463759939291864, - 0.0009254779772329933, - 0.00030908834306052194, - -0.00033072041687547837, - -0.0009242244673079876, - -0.001411151838185113, - -0.0017423327816480623, - -0.001885184212057672, - -0.001827180245955061, - -0.0015767572410517868, - -0.0011623018660948076, - -0.0006290757127071664, - -0.000034277486241399186, - 0.0005590116846630576, - 0.0010883547994854647, - 0.0014984460140958136, - 0.0017469004431845741, - 0.0018085733127357138, - 0.00167799070404902, - 0.0013699023886079994, - 0.0009178041117923993, - 0.00037029761935479554, - -0.00021414834948363428, - -0.0007733385753572961, - -0.0012479991812573157, - -0.0015880719715790936, - -0.0017577706864892022, - -0.0017393843977854672, - -0.0015352614436320124, - -0.0011668823340746201, - -0.0006738084260047715, - -0.00010821707107113083, - 0.00046927876230741587, - 0.0009969768021073388, - 0.001418713685742809, - 0.001689241246422699, - 0.0017799814611489096, - 0.0016808579160732414, - 0.001401938565514821, - 0.0009725070918705811, - 0.00043789676780500817, - -0.00014493157532811753, - -0.0007134285789517371, - -0.0012066408059786802, - -0.0015718235937901522, - -0.0017696752225057275, - -0.0017782857455891608, - -0.0015955792180418218, - -0.001239958862296361, - -0.00074891505028209, - -0.0001750373426426684, - 0.0004203972508663324, - 0.0009743368473658559, - 0.001427583798294352, - 0.0017300695180374488, - 0.0018474313131084748, - 0.0017665803736364497, - 0.0014959523321337515, - 0.0010620969968500548, - 0.0005087929550228872, - -0.00010287888971484906, - -0.0007071978327354896, - -0.0012439856702169039, - -0.0016485023632017621, - -0.0018816779265696613, - -0.0019137822019732152, - -0.0017410363220249359, - -0.0013797190792108685, - -0.0008669516848493666, - -0.0002564820252824784, - 0.00038702570052167175, - 0.0009946245737452108, - 0.0015005576753828536, - 0.0018494543772917837, - 0.002002271293795752, - 0.0019405399480025168, - 0.001668583653638679, - 0.0012133648938024923, - 0.0006218226272857692, - -0.00004396586555373278, - -0.0007131888239163173, - -0.0013137753201852866, - -0.0017802301804471922, - -0.002060772342951284, - -0.0021230164816546412, - -0.0019576726289922586, - -0.001579698202436061, - -0.0010269473735399154, - -0.0003566571442405154, - 0.00036046532149158213, - 0.001047910209033917, - 0.0016308959748307101, - 0.0020457879850275806, - 0.0022452826272775055, - 0.0022053677261764473, - 0.0019268362424314605, - 0.001436104198970914, - 0.000783301048300022, - 0.000036170113001030666, - -0.0007258491765731604, - -0.0014208542690678963, - -0.001973351045206107, - -0.0023218012876373207, - -0.002425430974535461, - -0.0022692307984843695, - -0.0018659822196923194, - -0.0012553733554405261, - -0.0005001275988053344, - 0.0003207533398910953, - 0.0011202470135660719, - 0.0018119245687932325, - 0.0023191095738080646, - 0.0025840361579153536, - 0.0025746366972740037, - 0.0022874947808971076, - 0.0017482590981993708, - 0.001010469529718797, - 0.00015080284754578053, - -0.0007401321025391216, - -0.0015677281601321103, - -0.002241555313336647, - -0.002685049904781321, - -0.002847415468166842, - -0.002706332216634437, - -0.0022693572465456347, - -0.0015807551810501244, - -0.0007073681818317356, - 0.00025858155245919815, - 0.0012156374711696657, - 0.0020601250277244316, - 0.002698864465909288, - 0.003058968365749582, - 0.003095930462187589, - 0.002799180323785052, - 0.0021938662312239255, - 0.0013387749941891575, - 0.00032073673872905727, - -0.0007540149497279023, - -0.0017707823148707424, - -0.0026186149816637165, - -0.0032023981947833136, - -0.0034533803843191375, - -0.0033370666249195324, - -0.002857634299302074, - -0.0020583398437455315, - -0.0010178803764328268, - 0.00015714303240366978, - 0.00134312158248896, - 0.0024123674642028033, - 0.0032466971416879246, - 0.003750429521389635, - 0.0038610298842211605, - 0.0035563826341158213, - 0.0028590450497876694, - 0.0018334905123878731, - 0.0005825119501486656, - -0.0007653431253644171, - -0.002066921044075508, - -0.00318050748566926, - -0.003981099599522876, - -0.004372950526822947, - -0.004302245531766623, - -0.0037634623427922525, - -0.0028010281264617036, - -0.0015063684237781073, - -0.000009312999857163947, - 0.0015348340498211675, - 0.002960705851903475, - 0.00411025264482529, - 0.004850497409323919, - 0.00508852374186094, - 0.004782051229146126, - 0.003945677268175109, - 0.002651861057270526, - 0.001024987337059874, - -0.000770898857558077, - -0.0025480549808695363, - -0.0041134371938038455, - -0.005289473467472686, - -0.0059345294154884536, - -0.005958790652492603, - -0.0053348632172493865, - -0.004104133685973202, - -0.0023757889373754375, - -0.0003154863263286177, - 0.0018679968592255536, - 0.003942664171895544, - 0.005680643711722206, - 0.006877057686831076, - 0.007378348952494577, - 0.007096715159487296, - 0.0060250187066667035, - 0.004239992353086172, - 0.0018984740602875209, - -0.0007749973681354589, - -0.0035085807133030038, - -0.0060094188052075385, - -0.007993440148818328, - -0.009215708970605762, - -0.00949819102645701, - -0.008752165260963618, - -0.006992771039067642, - -0.004343750499151247, - -0.0010313778997952953, - 0.002632475569255834, - 0.006276994042887084, - 0.009506525921431109, - 0.011939275012512792, - 0.013247115357734634, - 0.013192709905840089, - 0.011660378948982, - 0.008677354388180201, - 0.0044226259943339384, - -0.0007776763994276021, - -0.006468598011493521, - -0.01209866234178173, - -0.01705984630504337, - -0.020740073978089375, - -0.022572656974485978, - -0.022091348052909576, - -0.018974044064189968, - -0.01307870343426492, - -0.004467461602914141, - 0.006588374127832776, - 0.019617756508396658, - 0.03397630130130575, - 0.04888871785298119, - 0.06350208150118342, - 0.07694602080488765, - 0.08839667839951919, - 0.09713857345310689, - 0.10261800054825827, - 0.10448449122868045, - 0.10261800054825827, - 0.09713857345310689, - 0.08839667839951919, - 0.07694602080488765, - 0.06350208150118342, - 0.04888871785298119, - 0.03397630130130575, - 0.019617756508396658, - 0.006588374127832776, - -0.004467461602914141, - -0.01307870343426492, - -0.018974044064189968, - -0.022091348052909576, - -0.022572656974485978, - -0.020740073978089375, - -0.01705984630504337, - -0.01209866234178173, - -0.006468598011493521, - -0.0007776763994276021, - 0.0044226259943339384, - 0.008677354388180201, - 0.011660378948982, - 0.013192709905840089, - 0.013247115357734634, - 0.011939275012512792, - 0.009506525921431109, - 0.006276994042887084, - 0.002632475569255834, - -0.0010313778997952953, - -0.004343750499151247, - -0.006992771039067642, - -0.008752165260963618, - -0.00949819102645701, - -0.009215708970605762, - -0.007993440148818328, - -0.0060094188052075385, - -0.0035085807133030038, - -0.0007749973681354589, - 0.0018984740602875209, - 0.004239992353086172, - 0.0060250187066667035, - 0.007096715159487296, - 0.007378348952494577, - 0.006877057686831076, - 0.005680643711722206, - 0.003942664171895544, - 0.0018679968592255536, - -0.0003154863263286177, - -0.0023757889373754375, - -0.004104133685973202, - -0.0053348632172493865, - -0.005958790652492603, - -0.0059345294154884536, - -0.005289473467472686, - -0.0041134371938038455, - -0.0025480549808695363, - -0.000770898857558077, - 0.001024987337059874, - 0.002651861057270526, - 0.003945677268175109, - 0.004782051229146126, - 0.00508852374186094, - 0.004850497409323919, - 0.00411025264482529, - 0.002960705851903475, - 0.0015348340498211675, - -0.000009312999857163947, - -0.0015063684237781073, - -0.0028010281264617036, - -0.0037634623427922525, - -0.004302245531766623, - -0.004372950526822947, - -0.003981099599522876, - -0.00318050748566926, - -0.002066921044075508, - -0.0007653431253644171, - 0.0005825119501486656, - 0.0018334905123878731, - 0.0028590450497876694, - 0.0035563826341158213, - 0.0038610298842211605, - 0.003750429521389635, - 0.0032466971416879246, - 0.0024123674642028033, - 0.00134312158248896, - 0.00015714303240366978, - -0.0010178803764328268, - -0.0020583398437455315, - -0.002857634299302074, - -0.0033370666249195324, - -0.0034533803843191375, - -0.0032023981947833136, - -0.0026186149816637165, - -0.0017707823148707424, - -0.0007540149497279023, - 0.00032073673872905727, - 0.0013387749941891575, - 0.0021938662312239255, - 0.002799180323785052, - 0.003095930462187589, - 0.003058968365749582, - 0.002698864465909288, - 0.0020601250277244316, - 0.0012156374711696657, - 0.00025858155245919815, - -0.0007073681818317356, - -0.0015807551810501244, - -0.0022693572465456347, - -0.002706332216634437, - -0.002847415468166842, - -0.002685049904781321, - -0.002241555313336647, - -0.0015677281601321103, - -0.0007401321025391216, - 0.00015080284754578053, - 0.001010469529718797, - 0.0017482590981993708, - 0.0022874947808971076, - 0.0025746366972740037, - 0.0025840361579153536, - 0.0023191095738080646, - 0.0018119245687932325, - 0.0011202470135660719, - 0.0003207533398910953, - -0.0005001275988053344, - -0.0012553733554405261, - -0.0018659822196923194, - -0.0022692307984843695, - -0.002425430974535461, - -0.0023218012876373207, - -0.001973351045206107, - -0.0014208542690678963, - -0.0007258491765731604, - 0.000036170113001030666, - 0.000783301048300022, - 0.001436104198970914, - 0.0019268362424314605, - 0.0022053677261764473, - 0.0022452826272775055, - 0.0020457879850275806, - 0.0016308959748307101, - 0.001047910209033917, - 0.00036046532149158213, - -0.0003566571442405154, - -0.0010269473735399154, - -0.001579698202436061, - -0.0019576726289922586, - -0.0021230164816546412, - -0.002060772342951284, - -0.0017802301804471922, - -0.0013137753201852866, - -0.0007131888239163173, - -0.00004396586555373278, - 0.0006218226272857692, - 0.0012133648938024923, - 0.001668583653638679, - 0.0019405399480025168, - 0.002002271293795752, - 0.0018494543772917837, - 0.0015005576753828536, - 0.0009946245737452108, - 0.00038702570052167175, - -0.0002564820252824784, - -0.0008669516848493666, - -0.0013797190792108685, - -0.0017410363220249359, - -0.0019137822019732152, - -0.0018816779265696613, - -0.0016485023632017621, - -0.0012439856702169039, - -0.0007071978327354896, - -0.00010287888971484906, - 0.0005087929550228872, - 0.0010620969968500548, - 0.0014959523321337515, - 0.0017665803736364497, - 0.0018474313131084748, - 0.0017300695180374488, - 0.001427583798294352, - 0.0009743368473658559, - 0.0004203972508663324, - -0.0001750373426426684, - -0.00074891505028209, - -0.001239958862296361, - -0.0015955792180418218, - -0.0017782857455891608, - -0.0017696752225057275, - -0.0015718235937901522, - -0.0012066408059786802, - -0.0007134285789517371, - -0.00014493157532811753, - 0.00043789676780500817, - 0.0009725070918705811, - 0.001401938565514821, - 0.0016808579160732414, - 0.0017799814611489096, - 0.001689241246422699, - 0.001418713685742809, - 0.0009969768021073388, - 0.00046927876230741587, - -0.00010821707107113083, - -0.0006738084260047715, - -0.0011668823340746201, - -0.0015352614436320124, - -0.0017393843977854672, - -0.0017577706864892022, - -0.0015880719715790936, - -0.0012479991812573157, - -0.0007733385753572961, - -0.00021414834948363428, - 0.00037029761935479554, - 0.0009178041117923993, - 0.0013699023886079994, - 0.00167799070404902, - 0.0018085733127357138, - 0.0017469004431845741, - 0.0014984460140958136, - 0.0010883547994854647, - 0.0005590116846630576, - -0.000034277486241399186, - -0.0006290757127071664, - -0.0011623018660948076, - -0.0015767572410517868, - -0.001827180245955061, - -0.001885184212057672, - -0.0017423327816480623, - -0.001411151838185113, - -0.0009242244673079876, - -0.00033072041687547837, - 0.00030908834306052194, - 0.0009254779772329933, - 0.001463759939291864, - 0.00184518937028339, - 0.002054368304440125, - 0.0020485384828633777, - 0.0018238726340835366, - 0.0014076463254305174, - 0.0008397052861045773, - 0.00017203581592664565, - -0.0005293364966829129, - -0.0011899674040168643, - -0.0017380160407736585, - -0.0021136819231921474, - -0.002274533536918245, - -0.0021983440926621103, - -0.0018849613428995964, - -0.0013572018806166464, - -0.0006600057708364918, - 0.0001428944628485573, - 0.0009750187684900944, - 0.0017550758096436146, - 0.002405247908616018, - 0.002858826770125135, - 0.003066845756573675, - 0.003003052919640715, - 0.0026667562654551246, - 0.0020838354221374305, - 0.0013056441129537509, - 0.00040506185948651675, - -0.0005294931107453994, - -0.001401086778987431, - -0.00211370881796881, - -0.002579780077995427, - -0.00272885000805846, - -0.0025137861299055595, - -0.0019147615431778597, - -0.0009423273945619157, - 0.00036406537300421297, - 0.0019376716631701368, - 0.003689888284206567, - 0.005516680433668245, - 0.007306289027941143, - 0.008947826461137941, - 0.010339492321850014, - 0.011395973228693276, - 0.012054670302231996, - 0.01227985703081096, - 0.012064461696610605, - 0.011429759533610568, - 0.010422968037142797, - 0.009112816853143859, - 0.007583700132903604, - 0.005929041211240287, - 0.004244279668461443, - 0.002620018618160862, - 0.0011360279713313209, - -0.00014348647168516363, - -0.0011732914244696805, - -0.0019288301578398123, - -0.0024058915455907196, - -0.002619226598223891, - -0.002599310268302821, - -0.0023884346335371165, - -0.006122920352830965 + -0.006122920352830965f, + -0.0023884346335371165f, + -0.002599310268302821f, + -0.002619226598223891f, + -0.0024058915455907196f, + -0.0019288301578398123f, + -0.0011732914244696805f, + -0.00014348647168516363f, + 0.0011360279713313209f, + 0.002620018618160862f, + 0.004244279668461443f, + 0.005929041211240287f, + 0.007583700132903604f, + 0.009112816853143859f, + 0.010422968037142797f, + 0.011429759533610568f, + 0.012064461696610605f, + 0.01227985703081096f, + 0.012054670302231996f, + 0.011395973228693276f, + 0.010339492321850014f, + 0.008947826461137941f, + 0.007306289027941143f, + 0.005516680433668245f, + 0.003689888284206567f, + 0.0019376716631701368f, + 0.00036406537300421297f, + -0.0009423273945619157f, + -0.0019147615431778597f, + -0.0025137861299055595f, + -0.00272885000805846f, + -0.002579780077995427f, + -0.00211370881796881f, + -0.001401086778987431f, + -0.0005294931107453994f, + 0.00040506185948651675f, + 0.0013056441129537509f, + 0.0020838354221374305f, + 0.0026667562654551246f, + 0.003003052919640715f, + 0.003066845756573675f, + 0.002858826770125135f, + 0.002405247908616018f, + 0.0017550758096436146f, + 0.0009750187684900944f, + 0.0001428944628485573f, + -0.0006600057708364918f, + -0.0013572018806166464f, + -0.0018849613428995964f, + -0.0021983440926621103f, + -0.002274533536918245f, + -0.0021136819231921474f, + -0.0017380160407736585f, + -0.0011899674040168643f, + -0.0005293364966829129f, + 0.00017203581592664565f, + 0.0008397052861045773f, + 0.0014076463254305174f, + 0.0018238726340835366f, + 0.0020485384828633777f, + 0.002054368304440125f, + 0.00184518937028339f, + 0.001463759939291864f, + 0.0009254779772329933f, + 0.00030908834306052194f, + -0.00033072041687547837f, + -0.0009242244673079876f, + -0.001411151838185113f, + -0.0017423327816480623f, + -0.001885184212057672f, + -0.001827180245955061f, + -0.0015767572410517868f, + -0.0011623018660948076f, + -0.0006290757127071664f, + -0.000034277486241399186f, + 0.0005590116846630576f, + 0.0010883547994854647f, + 0.0014984460140958136f, + 0.0017469004431845741f, + 0.0018085733127357138f, + 0.00167799070404902f, + 0.0013699023886079994f, + 0.0009178041117923993f, + 0.00037029761935479554f, + -0.00021414834948363428f, + -0.0007733385753572961f, + -0.0012479991812573157f, + -0.0015880719715790936f, + -0.0017577706864892022f, + -0.0017393843977854672f, + -0.0015352614436320124f, + -0.0011668823340746201f, + -0.0006738084260047715f, + -0.00010821707107113083f, + 0.00046927876230741587f, + 0.0009969768021073388f, + 0.001418713685742809f, + 0.001689241246422699f, + 0.0017799814611489096f, + 0.0016808579160732414f, + 0.001401938565514821f, + 0.0009725070918705811f, + 0.00043789676780500817f, + -0.00014493157532811753f, + -0.0007134285789517371f, + -0.0012066408059786802f, + -0.0015718235937901522f, + -0.0017696752225057275f, + -0.0017782857455891608f, + -0.0015955792180418218f, + -0.001239958862296361f, + -0.00074891505028209f, + -0.0001750373426426684f, + 0.0004203972508663324f, + 0.0009743368473658559f, + 0.001427583798294352f, + 0.0017300695180374488f, + 0.0018474313131084748f, + 0.0017665803736364497f, + 0.0014959523321337515f, + 0.0010620969968500548f, + 0.0005087929550228872f, + -0.00010287888971484906f, + -0.0007071978327354896f, + -0.0012439856702169039f, + -0.0016485023632017621f, + -0.0018816779265696613f, + -0.0019137822019732152f, + -0.0017410363220249359f, + -0.0013797190792108685f, + -0.0008669516848493666f, + -0.0002564820252824784f, + 0.00038702570052167175f, + 0.0009946245737452108f, + 0.0015005576753828536f, + 0.0018494543772917837f, + 0.002002271293795752f, + 0.0019405399480025168f, + 0.001668583653638679f, + 0.0012133648938024923f, + 0.0006218226272857692f, + -0.00004396586555373278f, + -0.0007131888239163173f, + -0.0013137753201852866f, + -0.0017802301804471922f, + -0.002060772342951284f, + -0.0021230164816546412f, + -0.0019576726289922586f, + -0.001579698202436061f, + -0.0010269473735399154f, + -0.0003566571442405154f, + 0.00036046532149158213f, + 0.001047910209033917f, + 0.0016308959748307101f, + 0.0020457879850275806f, + 0.0022452826272775055f, + 0.0022053677261764473f, + 0.0019268362424314605f, + 0.001436104198970914f, + 0.000783301048300022f, + 0.000036170113001030666f, + -0.0007258491765731604f, + -0.0014208542690678963f, + -0.001973351045206107f, + -0.0023218012876373207f, + -0.002425430974535461f, + -0.0022692307984843695f, + -0.0018659822196923194f, + -0.0012553733554405261f, + -0.0005001275988053344f, + 0.0003207533398910953f, + 0.0011202470135660719f, + 0.0018119245687932325f, + 0.0023191095738080646f, + 0.0025840361579153536f, + 0.0025746366972740037f, + 0.0022874947808971076f, + 0.0017482590981993708f, + 0.001010469529718797f, + 0.00015080284754578053f, + -0.0007401321025391216f, + -0.0015677281601321103f, + -0.002241555313336647f, + -0.002685049904781321f, + -0.002847415468166842f, + -0.002706332216634437f, + -0.0022693572465456347f, + -0.0015807551810501244f, + -0.0007073681818317356f, + 0.00025858155245919815f, + 0.0012156374711696657f, + 0.0020601250277244316f, + 0.002698864465909288f, + 0.003058968365749582f, + 0.003095930462187589f, + 0.002799180323785052f, + 0.0021938662312239255f, + 0.0013387749941891575f, + 0.00032073673872905727f, + -0.0007540149497279023f, + -0.0017707823148707424f, + -0.0026186149816637165f, + -0.0032023981947833136f, + -0.0034533803843191375f, + -0.0033370666249195324f, + -0.002857634299302074f, + -0.0020583398437455315f, + -0.0010178803764328268f, + 0.00015714303240366978f, + 0.00134312158248896f, + 0.0024123674642028033f, + 0.0032466971416879246f, + 0.003750429521389635f, + 0.0038610298842211605f, + 0.0035563826341158213f, + 0.0028590450497876694f, + 0.0018334905123878731f, + 0.0005825119501486656f, + -0.0007653431253644171f, + -0.002066921044075508f, + -0.00318050748566926f, + -0.003981099599522876f, + -0.004372950526822947f, + -0.004302245531766623f, + -0.0037634623427922525f, + -0.0028010281264617036f, + -0.0015063684237781073f, + -0.000009312999857163947f, + 0.0015348340498211675f, + 0.002960705851903475f, + 0.00411025264482529f, + 0.004850497409323919f, + 0.00508852374186094f, + 0.004782051229146126f, + 0.003945677268175109f, + 0.002651861057270526f, + 0.001024987337059874f, + -0.000770898857558077f, + -0.0025480549808695363f, + -0.0041134371938038455f, + -0.005289473467472686f, + -0.0059345294154884536f, + -0.005958790652492603f, + -0.0053348632172493865f, + -0.004104133685973202f, + -0.0023757889373754375f, + -0.0003154863263286177f, + 0.0018679968592255536f, + 0.003942664171895544f, + 0.005680643711722206f, + 0.006877057686831076f, + 0.007378348952494577f, + 0.007096715159487296f, + 0.0060250187066667035f, + 0.004239992353086172f, + 0.0018984740602875209f, + -0.0007749973681354589f, + -0.0035085807133030038f, + -0.0060094188052075385f, + -0.007993440148818328f, + -0.009215708970605762f, + -0.00949819102645701f, + -0.008752165260963618f, + -0.006992771039067642f, + -0.004343750499151247f, + -0.0010313778997952953f, + 0.002632475569255834f, + 0.006276994042887084f, + 0.009506525921431109f, + 0.011939275012512792f, + 0.013247115357734634f, + 0.013192709905840089f, + 0.011660378948982f, + 0.008677354388180201f, + 0.0044226259943339384f, + -0.0007776763994276021f, + -0.006468598011493521f, + -0.01209866234178173f, + -0.01705984630504337f, + -0.020740073978089375f, + -0.022572656974485978f, + -0.022091348052909576f, + -0.018974044064189968f, + -0.01307870343426492f, + -0.004467461602914141f, + 0.006588374127832776f, + 0.019617756508396658f, + 0.03397630130130575f, + 0.04888871785298119f, + 0.06350208150118342f, + 0.07694602080488765f, + 0.08839667839951919f, + 0.09713857345310689f, + 0.10261800054825827f, + 0.10448449122868045f, + 0.10261800054825827f, + 0.09713857345310689f, + 0.08839667839951919f, + 0.07694602080488765f, + 0.06350208150118342f, + 0.04888871785298119f, + 0.03397630130130575f, + 0.019617756508396658f, + 0.006588374127832776f, + -0.004467461602914141f, + -0.01307870343426492f, + -0.018974044064189968f, + -0.022091348052909576f, + -0.022572656974485978f, + -0.020740073978089375f, + -0.01705984630504337f, + -0.01209866234178173f, + -0.006468598011493521f, + -0.0007776763994276021f, + 0.0044226259943339384f, + 0.008677354388180201f, + 0.011660378948982f, + 0.013192709905840089f, + 0.013247115357734634f, + 0.011939275012512792f, + 0.009506525921431109f, + 0.006276994042887084f, + 0.002632475569255834f, + -0.0010313778997952953f, + -0.004343750499151247f, + -0.006992771039067642f, + -0.008752165260963618f, + -0.00949819102645701f, + -0.009215708970605762f, + -0.007993440148818328f, + -0.0060094188052075385f, + -0.0035085807133030038f, + -0.0007749973681354589f, + 0.0018984740602875209f, + 0.004239992353086172f, + 0.0060250187066667035f, + 0.007096715159487296f, + 0.007378348952494577f, + 0.006877057686831076f, + 0.005680643711722206f, + 0.003942664171895544f, + 0.0018679968592255536f, + -0.0003154863263286177f, + -0.0023757889373754375f, + -0.004104133685973202f, + -0.0053348632172493865f, + -0.005958790652492603f, + -0.0059345294154884536f, + -0.005289473467472686f, + -0.0041134371938038455f, + -0.0025480549808695363f, + -0.000770898857558077f, + 0.001024987337059874f, + 0.002651861057270526f, + 0.003945677268175109f, + 0.004782051229146126f, + 0.00508852374186094f, + 0.004850497409323919f, + 0.00411025264482529f, + 0.002960705851903475f, + 0.0015348340498211675f, + -0.000009312999857163947f, + -0.0015063684237781073f, + -0.0028010281264617036f, + -0.0037634623427922525f, + -0.004302245531766623f, + -0.004372950526822947f, + -0.003981099599522876f, + -0.00318050748566926f, + -0.002066921044075508f, + -0.0007653431253644171f, + 0.0005825119501486656f, + 0.0018334905123878731f, + 0.0028590450497876694f, + 0.0035563826341158213f, + 0.0038610298842211605f, + 0.003750429521389635f, + 0.0032466971416879246f, + 0.0024123674642028033f, + 0.00134312158248896f, + 0.00015714303240366978f, + -0.0010178803764328268f, + -0.0020583398437455315f, + -0.002857634299302074f, + -0.0033370666249195324f, + -0.0034533803843191375f, + -0.0032023981947833136f, + -0.0026186149816637165f, + -0.0017707823148707424f, + -0.0007540149497279023f, + 0.00032073673872905727f, + 0.0013387749941891575f, + 0.0021938662312239255f, + 0.002799180323785052f, + 0.003095930462187589f, + 0.003058968365749582f, + 0.002698864465909288f, + 0.0020601250277244316f, + 0.0012156374711696657f, + 0.00025858155245919815f, + -0.0007073681818317356f, + -0.0015807551810501244f, + -0.0022693572465456347f, + -0.002706332216634437f, + -0.002847415468166842f, + -0.002685049904781321f, + -0.002241555313336647f, + -0.0015677281601321103f, + -0.0007401321025391216f, + 0.00015080284754578053f, + 0.001010469529718797f, + 0.0017482590981993708f, + 0.0022874947808971076f, + 0.0025746366972740037f, + 0.0025840361579153536f, + 0.0023191095738080646f, + 0.0018119245687932325f, + 0.0011202470135660719f, + 0.0003207533398910953f, + -0.0005001275988053344f, + -0.0012553733554405261f, + -0.0018659822196923194f, + -0.0022692307984843695f, + -0.002425430974535461f, + -0.0023218012876373207f, + -0.001973351045206107f, + -0.0014208542690678963f, + -0.0007258491765731604f, + 0.000036170113001030666f, + 0.000783301048300022f, + 0.001436104198970914f, + 0.0019268362424314605f, + 0.0022053677261764473f, + 0.0022452826272775055f, + 0.0020457879850275806f, + 0.0016308959748307101f, + 0.001047910209033917f, + 0.00036046532149158213f, + -0.0003566571442405154f, + -0.0010269473735399154f, + -0.001579698202436061f, + -0.0019576726289922586f, + -0.0021230164816546412f, + -0.002060772342951284f, + -0.0017802301804471922f, + -0.0013137753201852866f, + -0.0007131888239163173f, + -0.00004396586555373278f, + 0.0006218226272857692f, + 0.0012133648938024923f, + 0.001668583653638679f, + 0.0019405399480025168f, + 0.002002271293795752f, + 0.0018494543772917837f, + 0.0015005576753828536f, + 0.0009946245737452108f, + 0.00038702570052167175f, + -0.0002564820252824784f, + -0.0008669516848493666f, + -0.0013797190792108685f, + -0.0017410363220249359f, + -0.0019137822019732152f, + -0.0018816779265696613f, + -0.0016485023632017621f, + -0.0012439856702169039f, + -0.0007071978327354896f, + -0.00010287888971484906f, + 0.0005087929550228872f, + 0.0010620969968500548f, + 0.0014959523321337515f, + 0.0017665803736364497f, + 0.0018474313131084748f, + 0.0017300695180374488f, + 0.001427583798294352f, + 0.0009743368473658559f, + 0.0004203972508663324f, + -0.0001750373426426684f, + -0.00074891505028209f, + -0.001239958862296361f, + -0.0015955792180418218f, + -0.0017782857455891608f, + -0.0017696752225057275f, + -0.0015718235937901522f, + -0.0012066408059786802f, + -0.0007134285789517371f, + -0.00014493157532811753f, + 0.00043789676780500817f, + 0.0009725070918705811f, + 0.001401938565514821f, + 0.0016808579160732414f, + 0.0017799814611489096f, + 0.001689241246422699f, + 0.001418713685742809f, + 0.0009969768021073388f, + 0.00046927876230741587f, + -0.00010821707107113083f, + -0.0006738084260047715f, + -0.0011668823340746201f, + -0.0015352614436320124f, + -0.0017393843977854672f, + -0.0017577706864892022f, + -0.0015880719715790936f, + -0.0012479991812573157f, + -0.0007733385753572961f, + -0.00021414834948363428f, + 0.00037029761935479554f, + 0.0009178041117923993f, + 0.0013699023886079994f, + 0.00167799070404902f, + 0.0018085733127357138f, + 0.0017469004431845741f, + 0.0014984460140958136f, + 0.0010883547994854647f, + 0.0005590116846630576f, + -0.000034277486241399186f, + -0.0006290757127071664f, + -0.0011623018660948076f, + -0.0015767572410517868f, + -0.001827180245955061f, + -0.001885184212057672f, + -0.0017423327816480623f, + -0.001411151838185113f, + -0.0009242244673079876f, + -0.00033072041687547837f, + 0.00030908834306052194f, + 0.0009254779772329933f, + 0.001463759939291864f, + 0.00184518937028339f, + 0.002054368304440125f, + 0.0020485384828633777f, + 0.0018238726340835366f, + 0.0014076463254305174f, + 0.0008397052861045773f, + 0.00017203581592664565f, + -0.0005293364966829129f, + -0.0011899674040168643f, + -0.0017380160407736585f, + -0.0021136819231921474f, + -0.002274533536918245f, + -0.0021983440926621103f, + -0.0018849613428995964f, + -0.0013572018806166464f, + -0.0006600057708364918f, + 0.0001428944628485573f, + 0.0009750187684900944f, + 0.0017550758096436146f, + 0.002405247908616018f, + 0.002858826770125135f, + 0.003066845756573675f, + 0.003003052919640715f, + 0.0026667562654551246f, + 0.0020838354221374305f, + 0.0013056441129537509f, + 0.00040506185948651675f, + -0.0005294931107453994f, + -0.001401086778987431f, + -0.00211370881796881f, + -0.002579780077995427f, + -0.00272885000805846f, + -0.0025137861299055595f, + -0.0019147615431778597f, + -0.0009423273945619157f, + 0.00036406537300421297f, + 0.0019376716631701368f, + 0.003689888284206567f, + 0.005516680433668245f, + 0.007306289027941143f, + 0.008947826461137941f, + 0.010339492321850014f, + 0.011395973228693276f, + 0.012054670302231996f, + 0.01227985703081096f, + 0.012064461696610605f, + 0.011429759533610568f, + 0.010422968037142797f, + 0.009112816853143859f, + 0.007583700132903604f, + 0.005929041211240287f, + 0.004244279668461443f, + 0.002620018618160862f, + 0.0011360279713313209f, + -0.00014348647168516363f, + -0.0011732914244696805f, + -0.0019288301578398123f, + -0.0024058915455907196f, + -0.002619226598223891f, + -0.002599310268302821f, + -0.0023884346335371165f, + -0.006122920352830965f } ); - double[] mCoefficients; + float[] mCoefficients; - private Filters( double[] coefficients ) + private Filters( float[] coefficients ) { mCoefficients = coefficients; } - public double[] getCoefficients() + public float[] getCoefficients() { return mCoefficients; } diff --git a/src/dsp/filter/FloatFIRFilter.java b/src/dsp/filter/FloatFIRFilter.java index c2c8d69e..8355fbf0 100644 --- a/src/dsp/filter/FloatFIRFilter.java +++ b/src/dsp/filter/FloatFIRFilter.java @@ -27,13 +27,13 @@ public class FloatFIRFilter implements RealSampleListener private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double[] mCoefficients; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; - private double mGain; + private float mGain; - public FloatFIRFilter( double[] coefficients, double gain ) + public FloatFIRFilter( float[] coefficients, float gain ) { mCoefficients = coefficients; mBuffer = new ArrayList(); @@ -86,7 +86,7 @@ public class FloatFIRFilter implements RealSampleListener //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double accumulator = 0.0d; + float accumulator = 0.0f; //Start with the center tap value accumulator += mCoefficients[ mCenterCoefficient ] * @@ -103,7 +103,7 @@ public class FloatFIRFilter implements RealSampleListener //We're almost finished ... apply gain, cast the doubles to shorts and //return the value - return (float)( accumulator * mGain ); + return accumulator * mGain; } /** diff --git a/src/dsp/filter/FloatHalfBandFilter.java b/src/dsp/filter/FloatHalfBandFilter.java index e1a47392..56fae571 100644 --- a/src/dsp/filter/FloatHalfBandFilter.java +++ b/src/dsp/filter/FloatHalfBandFilter.java @@ -27,8 +27,8 @@ public class FloatHalfBandFilter implements RealSampleListener private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -50,7 +50,7 @@ public class FloatHalfBandFilter implements RealSampleListener * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public FloatHalfBandFilter( Filters filter, double gain ) + public FloatHalfBandFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); mBuffer = new ArrayList(); @@ -97,7 +97,7 @@ public class FloatHalfBandFilter implements RealSampleListener { //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double accumulator = 0; + float accumulator = 0; //Start with the center tap value accumulator += mCoefficients[ mCenterCoefficient ] * @@ -112,7 +112,7 @@ public class FloatHalfBandFilter implements RealSampleListener mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ) ); } - //We're almost finished ... apply gain, cast the doubles to shorts and + //We're almost finished ... apply gain, cast the floats to shorts and //send it on it's merry way if( mListener != null ) { diff --git a/src/dsp/filter/FloatHalfBandNoDecimateFilter.java b/src/dsp/filter/FloatHalfBandNoDecimateFilter.java index 57937df1..6f38db1f 100644 --- a/src/dsp/filter/FloatHalfBandNoDecimateFilter.java +++ b/src/dsp/filter/FloatHalfBandNoDecimateFilter.java @@ -27,8 +27,8 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -48,7 +48,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public FloatHalfBandNoDecimateFilter( Filters filter, double gain ) + public FloatHalfBandNoDecimateFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); mBuffer = new ArrayList(); @@ -88,7 +88,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double accumulator = 0; + float accumulator = 0; //Start with the center tap value accumulator += mCoefficients[ mCenterCoefficient ] * @@ -103,7 +103,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ) ); } - //We're almost finished ... apply gain, cast the doubles to floats and + //We're almost finished ... apply gain, cast the floats to floats and //send it on it's merry way if( mListener != null ) { diff --git a/src/dsp/filter/ComplexPrimeCICDecimate.java b/src/dsp/filter/cic/ComplexPrimeCICDecimate.java similarity index 75% rename from src/dsp/filter/ComplexPrimeCICDecimate.java rename to src/dsp/filter/cic/ComplexPrimeCICDecimate.java index 8bdd6bd5..33ee6149 100644 --- a/src/dsp/filter/ComplexPrimeCICDecimate.java +++ b/src/dsp/filter/cic/ComplexPrimeCICDecimate.java @@ -1,22 +1,38 @@ -package dsp.filter; +/******************************************************************************* + * 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 + ******************************************************************************/ +package dsp.filter.cic; import java.util.ArrayList; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.ComplexBuffer; import sample.complex.ComplexSampleListener; +import sample.complex.ComplexToComplexBufferAssembler; import sample.decimator.ComplexDecimator; +import dsp.filter.FilterFactory; +import dsp.filter.Filters; import dsp.filter.Window.WindowType; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.filter.halfband.complex.HalfBandFilter_CB_CB; public class ComplexPrimeCICDecimate { - private final static Logger mLog = - LoggerFactory.getLogger( ComplexPrimeCICDecimate.class ); - public static int[] PRIMES = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53, 59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151, 157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251, @@ -31,9 +47,7 @@ public class ComplexPrimeCICDecimate private DecimatingStage mFirstDecimatingStage; private Output mOutput; - - private Listener mListener; - + /** * Non-Recursive Prime-Factor CIC Filter with float sample array inputs and * decimated, single paired i/q float sample output. @@ -55,6 +69,7 @@ public class ComplexPrimeCICDecimate { assert( decimation <= 700 ); + List stageSizes = getPrimeFactors( decimation ); for( int x = 0; x < stageSizes.size(); x++ ) @@ -89,12 +104,17 @@ public class ComplexPrimeCICDecimate } mDecimatingStages.clear(); + mDecimatingStages = null; + mFirstDecimatingStage = null; + + mOutput.dispose(); + mOutput = null; } /** * Adds a listener to receive the output of this CIC decimation filter */ - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mOutput.setListener( listener ); } @@ -102,9 +122,9 @@ public class ComplexPrimeCICDecimate /** * Removes listener from output of this CIC decimation filter */ - public void removeListener( Listener listener ) + public void removeListener() { - mOutput.removeListener( listener ); + mOutput.removeListener(); } /** @@ -143,14 +163,17 @@ public class ComplexPrimeCICDecimate /** * Primary input method for receiving sample arrays composed as I,Q,I,Q, etc. */ - public void receive( float[] samples ) + public void receive( ComplexBuffer buffer ) { - for( int x = 0; x < samples.length; x += 2 ) - { - mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] ); - } - - samples = null; + if( mFirstDecimatingStage != null ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] ); + } + } } /** @@ -326,22 +349,33 @@ public class ComplexPrimeCICDecimate */ public class Output implements ComplexSampleListener { - private ComplexFilter mCleanupFilter; - private ComplexHalfBandNoDecimateFilter mHalfBandFilter = - new ComplexHalfBandNoDecimateFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 0.4 ); + /* Decimated output buffers will contain 1024 samples */ + private ComplexToComplexBufferAssembler mAssembler = + new ComplexToComplexBufferAssembler( 2048 ); + + private ComplexFIRFilter_CB_CB mCleanupFilter; + private HalfBandFilter_CB_CB mHalfBandFilter = new HalfBandFilter_CB_CB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false ); public Output( int outputSampleRate, int passFrequency, int attenuation, WindowType windowType ) { - mCleanupFilter = new ComplexFIRFilter( FilterFactory + mCleanupFilter = new ComplexFIRFilter_CB_CB( FilterFactory .getCICCleanupFilter( outputSampleRate, passFrequency, attenuation, - windowType ), 0.4d ); + windowType ), 0.4f ); + mAssembler.setListener( mCleanupFilter ); mCleanupFilter.setListener( mHalfBandFilter ); } + + public void dispose() + { + mAssembler.dispose(); + mCleanupFilter.dispose(); + mHalfBandFilter.dispose(); + } /** * Receiver method for the output adapter to receive a filtered, @@ -349,15 +383,15 @@ public class ComplexPrimeCICDecimate * and output the sample values as a complex sample. */ @Override - public void receive( float left, float right ) + public void receive( float inphase, float quadrature ) { - mCleanupFilter.receive( new ComplexSample( left, right ) ); + mAssembler.receive( inphase, quadrature ); } /** * Adds a listener to receive output samples */ - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mHalfBandFilter.setListener( listener ); } @@ -365,14 +399,9 @@ public class ComplexPrimeCICDecimate /** * Removes the listener from receiving output samples */ - public void removeListener( Listener listener ) + public void removeListener() { - mHalfBandFilter.setListener( null ); + mHalfBandFilter.removeListener(); } - - public Listener getListener() - { - return mHalfBandFilter.getListener(); - } } } diff --git a/src/decode/lj1200/LJ1200ActivitySummary.java b/src/dsp/filter/dc/AveragingDCRemovalFilter_RB.java similarity index 58% rename from src/decode/lj1200/LJ1200ActivitySummary.java rename to src/dsp/filter/dc/AveragingDCRemovalFilter_RB.java index 2171d2c8..a2fc95f4 100644 --- a/src/decode/lj1200/LJ1200ActivitySummary.java +++ b/src/dsp/filter/dc/AveragingDCRemovalFilter_RB.java @@ -15,46 +15,52 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package dsp.filter.dc; -import message.Message; import sample.Listener; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; +import sample.real.RealBuffer; -public class LJ1200ActivitySummary implements ActivitySummaryProvider, - Listener +public class AveragingDCRemovalFilter_RB extends DCRemovalFilter_RB + implements Listener { - private AliasList mAliasList; + private float mAverage; + private float mRatio; - public LJ1200ActivitySummary( AliasList list ) + public AveragingDCRemovalFilter_RB( float ratio ) { - mAliasList = list; + mRatio = ratio; } public void dispose() { + mListener = null; } + public void reset() + { + mAverage = 0.0f; + } + + public float filter( float sample ) + { + mAverage += mRatio * ( sample - mAverage ); + + return sample - mAverage; + } + @Override - public void receive( Message message ) - { - if( message instanceof LJ1200Message ) + public void receive( RealBuffer buffer ) + { + if( mListener != null ) { - LJ1200Message lj = ((LJ1200Message)message); + float[] samples = buffer.getSamples(); - //Do something here in the future + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + mListener.receive( buffer ); } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tLJ-1200I\n\n" ); - - return sb.toString(); - } + } } diff --git a/src/audio/SquelchListener.java b/src/dsp/filter/dc/DCRemovalFilter.java similarity index 68% rename from src/audio/SquelchListener.java rename to src/dsp/filter/dc/DCRemovalFilter.java index db55d6ab..ef2fadf2 100644 --- a/src/audio/SquelchListener.java +++ b/src/dsp/filter/dc/DCRemovalFilter.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,17 +15,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package audio; +package dsp.filter.dc; -public interface SquelchListener +public abstract class DCRemovalFilter { - /** - * Squelch Control: - */ - public void setSquelch( SquelchState state ); - - public enum SquelchState + private float mAverage; + private float mRatio; + + public DCRemovalFilter( float ratio ) { - UNSQUELCH, SQUELCH; + mRatio = ratio; } -} + + public abstract void dispose(); + + public void reset() + { + mAverage = 0.0f; + } + + public float filter( float sample ) + { + mAverage += mRatio * ( sample - mAverage ); + + return sample - mAverage; + } +} \ No newline at end of file diff --git a/src/dsp/filter/DCRemovalFilter.java b/src/dsp/filter/dc/DCRemovalFilterLyons.java similarity index 87% rename from src/dsp/filter/DCRemovalFilter.java rename to src/dsp/filter/dc/DCRemovalFilterLyons.java index 4806ccaa..d24aa5a9 100644 --- a/src/dsp/filter/DCRemovalFilter.java +++ b/src/dsp/filter/dc/DCRemovalFilterLyons.java @@ -15,22 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.filter; +package dsp.filter.dc; -import sample.Listener; import sample.real.RealSampleListener; /** * Implements the real-time general DC removal algorithm described on page * 762, figure 13-62d, in Understanding Digital Signal Processing 3e, Lyons. */ -public class DCRemovalFilter implements RealSampleListener +public class DCRemovalFilterLyons implements RealSampleListener { - private double mAlpha; + private float mAlpha; private float mPrevious; private RealSampleListener mListener; - public DCRemovalFilter( double alpha ) + public DCRemovalFilterLyons( float alpha ) { mAlpha = alpha; } @@ -43,7 +42,7 @@ public class DCRemovalFilter implements RealSampleListener @Override public void receive( float sample ) { - send( (float)( sample + ( mPrevious * mAlpha ) - mPrevious ) ); + send( sample + ( mPrevious * mAlpha ) - mPrevious ); mPrevious = sample; } diff --git a/src/dsp/filter/dc/DCRemovalFilter_RB.java b/src/dsp/filter/dc/DCRemovalFilter_RB.java new file mode 100644 index 00000000..3b7edd66 --- /dev/null +++ b/src/dsp/filter/dc/DCRemovalFilter_RB.java @@ -0,0 +1,31 @@ +package dsp.filter.dc; + +import sample.Listener; +import sample.real.RealBuffer; + +public abstract class DCRemovalFilter_RB implements Listener +{ + protected Listener mListener; + + public DCRemovalFilter_RB() + { + + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } + + public void dispose() + { + mListener = null; + } + + public abstract void reset(); +} diff --git a/src/dsp/filter/DCRemovalFilter2.java b/src/dsp/filter/dc/DCRemovalFilter_R_R.java similarity index 70% rename from src/dsp/filter/DCRemovalFilter2.java rename to src/dsp/filter/dc/DCRemovalFilter_R_R.java index 36827935..de4fd627 100644 --- a/src/dsp/filter/DCRemovalFilter2.java +++ b/src/dsp/filter/dc/DCRemovalFilter_R_R.java @@ -15,43 +15,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.filter; +package dsp.filter.dc; -import sample.Listener; import sample.real.RealSampleListener; -public class DCRemovalFilter2 implements RealSampleListener +public class DCRemovalFilter_R_R extends DCRemovalFilter implements RealSampleListener { - private double mAverage; - private double mRatio; - private RealSampleListener mListener; + RealSampleListener mListener; - public DCRemovalFilter2( double ratio ) + public DCRemovalFilter_R_R( float ratio ) { - mRatio = ratio; + super( ratio ); + } + + @Override + public void dispose() + { + mListener = null; } @Override public void receive( float sample ) { - mAverage += mRatio * ( (double)sample - mAverage ); - - send( (float)( sample - mAverage ) ); - } - - /** - * Sends the filtered sample to all registered listeners - */ - private void send( float sample ) - { if( mListener != null ) { - mListener.receive( sample ); + mListener.receive( filter( sample ) ); } - } + } public void setListener( RealSampleListener listener ) { mListener = listener; } + + public void removeListener() + { + mListener = null; + } } diff --git a/src/dsp/filter/DCRemovalFilter3.java b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter.java similarity index 86% rename from src/dsp/filter/DCRemovalFilter3.java rename to src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter.java index 43d331c6..8edcffc2 100644 --- a/src/dsp/filter/DCRemovalFilter3.java +++ b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter.java @@ -1,8 +1,8 @@ -package dsp.filter; +package dsp.filter.dc; import sample.real.RealSampleListener; -public class DCRemovalFilter3 implements RealSampleListener +public class IIRSinglePoleDCRemovalFilter implements RealSampleListener { private float mAlpha; private float mPreviousInput = 0.0f; @@ -19,7 +19,7 @@ public class DCRemovalFilter3 implements RealSampleListener * @param alpha 0.0 - 1.0 float - the closer alpha is to unity, the closer * the cutoff frequency is to DC. */ - public DCRemovalFilter3( float alpha ) + public IIRSinglePoleDCRemovalFilter( float alpha ) { mAlpha = alpha; } diff --git a/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java new file mode 100644 index 00000000..c4f4a371 --- /dev/null +++ b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java @@ -0,0 +1,58 @@ +package dsp.filter.dc; + +import sample.real.RealBuffer; + +public class IIRSinglePoleDCRemovalFilter_RB extends DCRemovalFilter_RB +{ + private float mAlpha; + private float mPreviousInput = 0.0f; + private float mPreviousOutput = 0.0f; + + /** + * IIR single-pole DC removal filter, as described by J M de Freitas in + * 29Jan2007 paper at: + * + * http://www.mathworks.com/matlabcentral/fileexchange/downloads/72134/download + * + * @param alpha 0.0 - 1.0 float - the closer alpha is to unity, the closer + * the cutoff frequency is to DC. + */ + public IIRSinglePoleDCRemovalFilter_RB( float alpha ) + { + mAlpha = alpha; + } + + @Override + public void receive( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + public float filter( float sample ) + { + float currentOutput = ( sample - mPreviousInput ) + + ( mAlpha * mPreviousOutput ); + + mPreviousInput = sample; + mPreviousOutput = currentOutput; + + return currentOutput; + } + + @Override + public void reset() + { + mPreviousInput = 0.0f; + mPreviousOutput = 0.0f; + } +} diff --git a/src/dsp/filter/equalizer/CMAEqualizer.java b/src/dsp/filter/equalizer/CMAEqualizer.java index 882367f6..bbf79332 100644 --- a/src/dsp/filter/equalizer/CMAEqualizer.java +++ b/src/dsp/filter/equalizer/CMAEqualizer.java @@ -4,9 +4,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class CMAEqualizer implements Listener +public class CMAEqualizer implements Listener { private final static Logger mLog = LoggerFactory.getLogger( CMAEqualizer.class ); @@ -14,14 +14,14 @@ public class CMAEqualizer implements Listener private float mModulus; private float mMu; - private ComplexSample mError = new ComplexSample( 0.0f, 0.0f ); + private Complex mError = new Complex( 0.0f, 0.0f ); - private ComplexSample[] mTaps = new ComplexSample[ TAP_COUNT ]; - private ComplexSample[] mBuffer = new ComplexSample[ TAP_COUNT ]; + private Complex[] mTaps = new Complex[ TAP_COUNT ]; + private Complex[] mBuffer = new Complex[ TAP_COUNT ]; private int mBufferPointer = 0; - private Listener mListener; + private Listener mListener; public CMAEqualizer( float modulus, float mu ) { @@ -31,21 +31,21 @@ public class CMAEqualizer implements Listener /* Set first tap to 1,0 and all other taps to 0,0 as a starting point. * This means that the output is solely determined by the last sample * entering the filter, initially. */ - mTaps[ 0 ] = new ComplexSample( 1.0f, 0.0f ); + mTaps[ 0 ] = new Complex( 1.0f, 0.0f ); for( int x = 1; x < TAP_COUNT; x++ ) { - mTaps[ x ] = new ComplexSample( 0.0f, 0.0f ); - mBuffer[ x ] = new ComplexSample( 0.0f, 0.0f ); + mTaps[ x ] = new Complex( 0.0f, 0.0f ); + mBuffer[ x ] = new Complex( 0.0f, 0.0f ); } } - private void updateTaps( ComplexSample sample ) + private void updateTaps( Complex sample ) { /* Calculate the error. The ideal sample magnitude is unity (1.0). * When the sample is not at unity, the error vector points in the * opposite direction with a magnitude equal to 1 - sample magnitude.*/ - mError = ComplexSample.multiply( sample, sample.norm() - mModulus ); + mError = Complex.multiply( sample, sample.norm() - mModulus ); /* Ensure we don't exceed unity */ mError.clip( 1.0f ); @@ -54,14 +54,14 @@ public class CMAEqualizer implements Listener { // tap -= mu * conj(sample) * error - mTaps[ x ] = ComplexSample.subtract( mTaps[ x ], - ComplexSample.multiply( ComplexSample.multiply( + mTaps[ x ] = Complex.subtract( mTaps[ x ], + Complex.multiply( Complex.multiply( mBuffer[ x ].conjugate(), mError ), mMu ) ); } } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer[ mBufferPointer ] = sample; @@ -69,7 +69,7 @@ public class CMAEqualizer implements Listener mBufferPointer = mBufferPointer % TAP_COUNT; - ComplexSample equalized = filter(); + Complex equalized = filter(); updateTaps( equalized ); @@ -81,13 +81,13 @@ public class CMAEqualizer implements Listener mLog.debug( "Tap 0:" + mTaps[ 0 ].toString() + " error:" + mError.toString() ); } - private ComplexSample filter() + private Complex filter() { - ComplexSample sum = new ComplexSample( 0.0f, 0.0f ); + Complex sum = new Complex( 0.0f, 0.0f ); for( int x = 0; x < TAP_COUNT; x++ ) { - sum = ComplexSample.add( sum, ComplexSample.multiply( + sum = Complex.add( sum, Complex.multiply( mBuffer[ ( x + mBufferPointer ) % TAP_COUNT ], mTaps[ TAP_COUNT - x - 1 ] ) ); } @@ -95,7 +95,7 @@ public class CMAEqualizer implements Listener return sum; } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } diff --git a/src/dsp/filter/fir/FIRFilter.java b/src/dsp/filter/fir/FIRFilter.java new file mode 100644 index 00000000..f0f19d06 --- /dev/null +++ b/src/dsp/filter/fir/FIRFilter.java @@ -0,0 +1,6 @@ +package dsp.filter.fir; + +public abstract class FIRFilter +{ + public abstract void dispose(); +} diff --git a/src/dsp/filter/fir/complex/ComplexFIRFilter.java b/src/dsp/filter/fir/complex/ComplexFIRFilter.java new file mode 100644 index 00000000..744c27b9 --- /dev/null +++ b/src/dsp/filter/fir/complex/ComplexFIRFilter.java @@ -0,0 +1,54 @@ +package dsp.filter.fir.complex; + +import sample.complex.Complex; +import dsp.filter.fir.FIRFilter; +import dsp.filter.fir.real.RealFIRFilter; + +public class ComplexFIRFilter extends FIRFilter +{ + private RealFIRFilter mIFilter; + private RealFIRFilter mQFilter; + + /** + * Complex FIR Filter for processing complex sample pairs. Wraps two real + * FIR filters for processing each of the inphase and quadrature samples. + * + * @param coefficients - filter taps + * @param gain - gain to apply to filtered outputs - use 1.0f for no gain + */ + public ComplexFIRFilter( float[] coefficients, float gain ) + { + mIFilter = new RealFIRFilter( coefficients, gain ); + mQFilter = new RealFIRFilter( coefficients, gain ); + } + + public float[] getCoefficients() + { + return mIFilter.getCoefficients(); + } + + public float filterInphase( float sample ) + { + return mIFilter.filter( sample ); + } + + public float filterQuadrature( float sample ) + { + return mQFilter.filter( sample ); + } + + public Complex filter( Complex sample ) + { + float i = filterInphase( sample.inphase() ); + float q = filterQuadrature( sample.quadrature() ); + + return new Complex( i, q ); + } + + @Override + public void dispose() + { + mIFilter.dispose(); + mQFilter.dispose(); + } +} diff --git a/src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java b/src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java new file mode 100644 index 00000000..259d631a --- /dev/null +++ b/src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java @@ -0,0 +1,54 @@ +package dsp.filter.fir.complex; + +import sample.Listener; +import sample.complex.ComplexBuffer; + +public class ComplexFIRFilter_CB_CB extends ComplexFIRFilter + implements Listener +{ + private Listener mListener; + + /** + * Complex FIR Filter for processing complex buffers. Wraps two real FIR + * filters for processing each of the inphase and quadrature samples. + * + * @param coefficients - odd length symmetric filter taps + * @param gain - gain value to apply to each of the filtered samples + */ + public ComplexFIRFilter_CB_CB( float[] coefficients, float gain ) + { + super( coefficients, gain ); + } + + public void dispose() + { + mListener = null; + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + samples[ x ] = filterInphase( samples[ x ] ); + samples[ x + 1 ] = filterQuadrature( samples[ x + 1 ] ); + } + + mListener.receive( buffer ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/fir/real/RealFIRFilter.java b/src/dsp/filter/fir/real/RealFIRFilter.java new file mode 100644 index 00000000..fa3e28e7 --- /dev/null +++ b/src/dsp/filter/fir/real/RealFIRFilter.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package dsp.filter.fir.real; + +import dsp.filter.fir.FIRFilter; + +public class RealFIRFilter extends FIRFilter +{ + private float[] mBuffer; + private int mBufferSize = 1; + private int mBufferPointer = 0; + private int[][] mIndexMap; + + private float[] mCoefficients; + private float mGain; + + /** + * Float sample FIR filter base class. + * + * @param coefficients - filter coefficients + * @param gain value to apply to the filtered output - use 1.0f for no gain + */ + public RealFIRFilter( float[] coefficients, float gain ) + { + mCoefficients = coefficients; + mGain = gain; + + mBufferSize = mCoefficients.length; + + mBuffer = new float[ mBufferSize ]; + mBufferPointer = mBufferSize - 1; + + generateIndexMap( mBufferSize ); + } + + public float[] getCoefficients() + { + return mCoefficients; + } + + @Override + public void dispose() + { + mCoefficients = null; + mIndexMap = null; + mBuffer = null; + } + + public float filter( float sample ) + { + mBuffer[ mBufferPointer ] = sample; + + float accumulator = 0.0f; + + for( int x = 0; x < mBufferSize; x++ ) + { + accumulator += mCoefficients[ x ] * + mBuffer[ mIndexMap[ mBufferPointer][ x ] ]; + } + + mBufferPointer--; + + if( mBufferPointer < 0 ) + { + mBufferPointer += mBufferSize; + } + + /* Apply gain and return the filtered value */ + return accumulator * mGain; + } + + /** + * Generates a circular buffer index map to support lookup of the translated + * index based on the current buffer pointer and the desired sample index. + */ + private void generateIndexMap( int size ) + { + mIndexMap = new int[ size ][ size ]; + + for( int x = 0; x < size; x++ ) + { + for( int y = 0; y < size; y++ ) + { + int z = x + y; + + mIndexMap[ x ][ y ] = z < size ? z : z - size; + } + } + } +} \ No newline at end of file diff --git a/src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java b/src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java new file mode 100644 index 00000000..7f9fc957 --- /dev/null +++ b/src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java @@ -0,0 +1,67 @@ +package dsp.filter.fir.real; + +import sample.Listener; +import sample.real.RealBuffer; + +public class RealFIRFilter_RB_RB extends RealFIRFilter + implements Listener +{ + private Listener mListener; + + /** + * Real Buffer Float FIR filter with streaming float buffer provider and + * listener interfaces. + * + * @param coefficients + * @param gain + */ + public RealFIRFilter_RB_RB( float[] coefficients, float gain ) + { + super( coefficients, gain ); + } + + @Override + public void dispose() + { + super.dispose(); + + mListener = null; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( filter( buffer ) ); + } + } + + /** + * Filters the buffer samples + * + * @param buffer + * @return + */ + public RealBuffer filter( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + return buffer; + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/fir/real/RealFIRFilter_R_R.java b/src/dsp/filter/fir/real/RealFIRFilter_R_R.java new file mode 100644 index 00000000..b00bf317 --- /dev/null +++ b/src/dsp/filter/fir/real/RealFIRFilter_R_R.java @@ -0,0 +1,56 @@ +package dsp.filter.fir.real; + +import sample.real.RealSampleListener; + +public class RealFIRFilter_R_R extends RealFIRFilter implements RealSampleListener +{ + private RealSampleListener mListener; + + /** + * Float FIR filter with streaming float provider and listener interfaces. + * + * @param coefficients + * @param gain + */ + public RealFIRFilter_R_R( float[] coefficients, float gain ) + { + super( coefficients, gain ); + } + + @Override + public void dispose() + { + super.dispose(); + + mListener = null; + } + + @Override + public void receive( float sample ) + { + if( mListener != null ) + { + mListener.receive( filter( sample ) ); + } + } + + public float[] filter( float[] samples ) + { + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + return samples; + } + + public void setListener( RealSampleListener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java b/src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java new file mode 100644 index 00000000..a2707208 --- /dev/null +++ b/src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java @@ -0,0 +1,117 @@ +package dsp.filter.halfband.complex; + +import sample.Listener; +import sample.complex.ComplexBuffer; +import dsp.filter.halfband.real.HalfBandFilter; + +public class HalfBandFilter_CB_CB implements Listener +{ + private HalfBandFilter mIFilter; + private HalfBandFilter mQFilter; + + private boolean mDecimate; + private boolean mDecimateFlag; + + private Listener mListener; + + /** + * + * @param coefficients + * @param gain + * @param decimate + */ + public HalfBandFilter_CB_CB( float[] coefficients, float gain, boolean decimate ) + { + mIFilter = new HalfBandFilter( coefficients, gain ); + mQFilter = new HalfBandFilter( coefficients, gain ); + + mDecimate = decimate; + } + + public void dispose() + { + mIFilter.dispose(); + mQFilter.dispose(); + + mListener = null; + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + if( mDecimate ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] decimated; + + if( half % 2 == 0 || mDecimateFlag ) + { + decimated = new float[ half ]; + } + else + { + /* If inbound buffer size is odd-length, then we have to + * adjust when the first operation is non-decimation, since + * that will produce an outbound buffer 1 sample larger */ + decimated = new float[ half + 1 ]; + } + + int decimatedPointer = 0; + + for( int x = 0; x < samples.length; x += 2 ) + { + /* Insert the sample but don't filter */ + if( mDecimateFlag ) + { + mIFilter.insert( samples[ x ] ); + mQFilter.insert( samples[ x + 1 ] ); + } + else + { + decimated[ decimatedPointer++ ] = mIFilter.filter( samples[ x ] ); + decimated[ decimatedPointer++ ] = mQFilter.filter( samples[ x + 1 ] ); + } + + /* Toggle the decimation flag for every sample */ + mDecimateFlag = !mDecimateFlag; + } + + if( mListener != null ) + { + mListener.receive( new ComplexBuffer( decimated ) ); + } + } + else + { + /* Non Decimate - filter each of the values and return the buffer */ + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + samples[ x ] = mIFilter.filter( samples[ x ] ); + samples[ x + 1 ] = mQFilter.filter( samples[ x + 1 ] ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/halfband/real/HalfBandFilter.java b/src/dsp/filter/halfband/real/HalfBandFilter.java new file mode 100644 index 00000000..7b84616f --- /dev/null +++ b/src/dsp/filter/halfband/real/HalfBandFilter.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * 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 dsp.filter.halfband.real; + +public class HalfBandFilter +{ + private float[] mBuffer; + private int mBufferSize = 1; //Temporary initial value + private int mBufferPointer = 0; + + private float mGain; + private float[] mCoefficients; + private int[][] mIndexMap; + + private int mCenterCoefficient; + private int mCenterCoefficientMapIndex; + + /** + * Half-Band filter for processing real float samples. + * + * Takes advantage of the 0-valued FIR half-band coefficents inherent + * in the half-band filter and does not calculate those coefficients. + * + * This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample. + * + * @param filter - filter coefficients + * @param gain - gain multiplier. Use 1.0 for unity/no gain + */ + public HalfBandFilter( float[] coefficients, float gain ) + { + mCoefficients = coefficients; + mGain = gain; + + mBufferSize = mCoefficients.length; + mBuffer = new float[ mBufferSize ]; + + generateIndexMap( mCoefficients.length ); + } + + public void dispose() + { + } + + /** + * Inserts the sample into the buffer without calculating a filtered value + */ + public void insert( float sample ) + { + mBuffer[ mBufferPointer ] = sample; + + mBufferPointer++; + + mBufferPointer = mBufferPointer % mBufferSize; + } + + /** + * Calculate the filtered value by applying the coefficients against + * the complex samples in mBuffer + */ + public float filter( float sample ) + { + insert( sample ); + + float accumulator = 0; + + //Start with the center tap value + accumulator += mCoefficients[ mCenterCoefficient ] * + mBuffer[ mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ]; + + //For the remaining coefficients, add the symmetric samples, oldest and newest + //first, then multiply by the single coefficient + for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 ) + { + accumulator += mCoefficients[ x ] * + ( mBuffer[ mIndexMap[ mBufferPointer ][ x ] ] + + mBuffer[ mIndexMap[ mBufferPointer ][ x + 1 ] ] ); + } + + return accumulator * mGain; + } + + /** + * Creates an n X (n + 1 / 2) index map enabling quick access to the + * circular buffer samples. + * + * As the buffer shifts right with each subsequent sample, we have to move + * the index pointers with it, for efficient access of the samples. + * + * The first array index value in the index map corresponds to the current + * buffer pointer location. + * + * The second array index value points to the samples that should be + * multiplied by the coefficients as follows: + * + * 0 = center tap sample, to be multiplied by center coefficient + * + * 0 = sample( 1 ) + * 1 = sample( size - 1 ) + * + * Indexes 0 and 1 will be multiplied by coefficient( 0 ). + * + * Subsequent indexes 3, 4, etc, point to the oldest and newest samples that + * correspond to the matching ( 3 ) coefficient index. + * + * @param odd-sized number of filter taps (ie coefficients) and buffer + */ + private void generateIndexMap( int size ) + { + //Ensure we have an odd size + assert( size % 2 == 1 ); + + int mapWidth = ( ( size + 1 ) / 2 ) + 1; + + //Set center tap index for coefficients array + mCenterCoefficient = ( size - 1 ) / 2; + mCenterCoefficientMapIndex = mCenterCoefficient + 1; + + mIndexMap = new int[ size ][ mapWidth ]; + + //Setup the first row, buffer pointer index 0, as a starting point + for( int x = 0; x < mapWidth - 2; x += 2 ) + { + mIndexMap[ 0 ][ x ] = x; + mIndexMap[ 0 ][ x + 1 ] = size - 1 - x; + } + + //Place center tap index in last element + mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient; + + //For each subsequent row, increment the previous row's value by 1, + //subtracting size as needed, to keep the values between 0 and size - 1 + for( int x = 1; x < size; x++ ) + { + for( int y = 0; y < mapWidth; y++ ) + { + mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; + + if( mIndexMap[ x ][ y ] >= size ) + { + mIndexMap[ x ][ y ] -= size; + } + } + } + } +} diff --git a/src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java b/src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java new file mode 100644 index 00000000..90414814 --- /dev/null +++ b/src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java @@ -0,0 +1,101 @@ +package dsp.filter.halfband.real; + +import sample.Listener; +import sample.real.RealBuffer; + +public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener +{ + private boolean mDecimate; + private boolean mDecimateFlag; + private Listener mListener; + + /** + * + * @param coefficients + * @param gain + * @param decimate + */ + public HalfBandFilter_RB_RB( float[] coefficients, float gain, boolean decimate ) + { + super( coefficients, gain ); + + mDecimate = decimate; + } + + public void dispose() + { + super.dispose(); + + mListener = null; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + if( mDecimate ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] decimated; + + if( half % 2 == 0 || mDecimateFlag ) + { + decimated = new float[ half ]; + } + else + { + /* If inbound buffer size is odd-length, then we have to + * adjust when the first operation is non-decimation, since + * that will produce an outbound buffer 1 sample larger */ + decimated = new float[ half + 1 ]; + } + + int decimatedPointer = 0; + + for( float sample: samples ) + { + /* Insert the sample but don't filter */ + if( mDecimateFlag ) + { + insert( sample ); + } + else + { + decimated[ decimatedPointer++ ] = filter( sample ); + } + + /* Toggle the decimation flag for every sample */ + mDecimateFlag = !mDecimateFlag; + } + + mListener.receive( new RealBuffer( decimated ) ); + } + else + { + /* Non Decimate - filter each of the values and return the buffer */ + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + mListener.receive( buffer ); + } + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/interpolator/QPSKInterpolator.java b/src/dsp/filter/interpolator/QPSKInterpolator.java deleted file mode 100644 index 6c096158..00000000 --- a/src/dsp/filter/interpolator/QPSKInterpolator.java +++ /dev/null @@ -1,61 +0,0 @@ -package dsp.filter.interpolator; - -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.complex.ComplexSample; - -public class QPSKInterpolator extends Interpolator -{ - private final static Logger mLog = LoggerFactory.getLogger( QPSKInterpolator.class ); - - private float mGain; - - public QPSKInterpolator( float gain ) - { - mGain = gain; - } - /** - * Calculates an interpolated value from the 8 samples beginning at the - * offset index. Uses the mu value to determine which of 128 filter kernels - * to use in order to closely approximate the ideal frequency response for - * the value of mu, ranging from 0.0 to 1.0, indicating the interpolated - * position in the sample set ranging from offset to offset + 7. - * - * @param samples - sample array of length at least offset + 7 - * - * @param mu - interpolated sample position between 0 and 1.0 - * - * @return - interpolated sample value - */ - public ComplexSample filter( ComplexSample[] samples, int offset, float mu ) - { - /* Ensure we have enough samples in the array */ - assert( samples.length >= offset + 7 ); - - /* Identify the filter bank that corresponds to mu */ - int index = (int)( NSTEPS * mu ); - - float realAccumulator = ( TAPS[ index ][ 7 ] * samples[ offset ].real() ); - realAccumulator += ( TAPS[ index ][ 6 ] * samples[ offset + 1 ].real() ); - realAccumulator += ( TAPS[ index ][ 5 ] * samples[ offset + 2 ].real() ); - realAccumulator += ( TAPS[ index ][ 4 ] * samples[ offset + 3 ].real() ); - realAccumulator += ( TAPS[ index ][ 3 ] * samples[ offset + 4 ].real() ); - realAccumulator += ( TAPS[ index ][ 2 ] * samples[ offset + 5 ].real() ); - realAccumulator += ( TAPS[ index ][ 1 ] * samples[ offset + 6 ].real() ); - realAccumulator += ( TAPS[ index ][ 0 ] * samples[ offset + 7 ].real() ); - - float imaginaryAccumulator = ( TAPS[ index ][ 7 ] * samples[ offset ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 6 ] * samples[ offset + 1 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 5 ] * samples[ offset + 2 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 4 ] * samples[ offset + 3 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 3 ] * samples[ offset + 4 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 2 ] * samples[ offset + 5 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 1 ] * samples[ offset + 6 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 0 ] * samples[ offset + 7 ].imaginary() ); - - return new ComplexSample( realAccumulator * mGain, imaginaryAccumulator * mGain ); - } -} diff --git a/src/dsp/filter/interpolator/RealInterpolator.java b/src/dsp/filter/interpolator/RealInterpolator.java new file mode 100644 index 00000000..f840a3ed --- /dev/null +++ b/src/dsp/filter/interpolator/RealInterpolator.java @@ -0,0 +1,58 @@ +package dsp.filter.interpolator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.complex.Complex; + +public class RealInterpolator extends Interpolator +{ + private final static Logger mLog = LoggerFactory.getLogger( RealInterpolator.class ); + + private float mGain; + + public RealInterpolator( float gain ) + { + mGain = gain; + } + /** + * Calculates an interpolated value from the 8 samples beginning at the + * offset index. Uses the mu value to determine which of 128 filter kernels + * to use in order to closely approximate the ideal frequency response for + * the value of mu, ranging from 0.0 to 1.0, indicating the interpolated + * position in the sample set ranging from offset to offset + 7. + * + * @param samples - sample array of length at least offset + 7 + * + * @param mu - interpolated sample position between 0 and 1.0 + * + * @return - interpolated sample value + */ + public float filter( float[] samples, int offset, float mu ) + { + /* Ensure we have enough samples in the array */ + assert( samples.length >= offset + 7 ); + + /* Identify the filter bank that corresponds to mu */ + int index = (int)( NSTEPS * mu ); + + float accumulator = ( TAPS[ index ][ 7 ] * samples[ offset ] ); + accumulator += ( TAPS[ index ][ 6 ] * samples[ offset + 1 ] ); + accumulator += ( TAPS[ index ][ 5 ] * samples[ offset + 2 ] ); + accumulator += ( TAPS[ index ][ 4 ] * samples[ offset + 3 ] ); + accumulator += ( TAPS[ index ][ 3 ] * samples[ offset + 4 ] ); + accumulator += ( TAPS[ index ][ 2 ] * samples[ offset + 5 ] ); + accumulator += ( TAPS[ index ][ 1 ] * samples[ offset + 6 ] ); + accumulator += ( TAPS[ index ][ 0 ] * samples[ offset + 7 ] ); + + return accumulator * mGain; + } + + public Complex filter( float[] iSamples, float[] qSamples, int offset, float mu ) + { + float i = filter( iSamples, offset, mu ); + float q = filter( qSamples, offset, mu ); + + return new Complex( i, q ); + } +} diff --git a/src/dsp/fm/FMDemodulator.java b/src/dsp/fm/FMDemodulator.java new file mode 100644 index 00000000..238915bb --- /dev/null +++ b/src/dsp/fm/FMDemodulator.java @@ -0,0 +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 + ******************************************************************************/ +package dsp.fm; + +public abstract class FMDemodulator +{ + private float mPreviousI = 0.0f; + private float mPreviousQ = 0.0f; + protected float mGain; + + public FMDemodulator( float gain ) + { + mGain = gain; + } + + public float demodulate( float currentI, float currentQ ) + { + /** + * Multiply the current sample against the complex conjugate of the + * previous sample to derive the phase delta between the two samples + * + * Negating the previous sample quadrature produces the conjugate + */ + double inphase = ( currentI * mPreviousI ) - ( currentQ * -mPreviousQ ); + double quadrature = ( currentQ * mPreviousI ) + ( currentI * -mPreviousQ ); + + double angle = 0.0f; + + //Check for divide by zero + if( inphase != 0 ) + { + /** + * Use the arc-tangent of quadrature divided by inphase to + * get the phase angle (+/-) which was directly manipulated by the + * original message waveform during the modulation. This value now + * serves as the instantaneous amplitude of the demodulated signal + */ + double denominator = 1.0d / inphase; + angle = Math.atan( (double)quadrature * denominator ); + } + + /** + * Store the current sample to use during the next iteration + */ + mPreviousI = currentI; + mPreviousQ = currentQ; + + return (float)( angle * mGain ); + } + + public abstract void dispose(); + + public void reset() + { + mPreviousI = 0.0f; + mPreviousQ = 0.0f; + } + + public void setGain( float gain ) + { + mGain = gain; + } +} \ No newline at end of file diff --git a/src/dsp/fm/FMDemodulator_CB.java b/src/dsp/fm/FMDemodulator_CB.java new file mode 100644 index 00000000..30ae35b0 --- /dev/null +++ b/src/dsp/fm/FMDemodulator_CB.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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 dsp.fm; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.wave.ComplexWaveRecorder; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.real.RateCounter_RB; +import sample.real.RealBuffer; + +public class FMDemodulator_CB extends FMDemodulator implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( FMDemodulator_CB.class ); + + private Listener mListener; + + private RateCounter_RB mRateCounter = new RateCounter_RB( "FM DEMOD OUTPUT" ); + + public FMDemodulator_CB( float gain ) + { + super( gain ); + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] demodulated = new float[ half ]; + + for( int x = 0; x < half; x++ ) + { + int index = x * 2; + + demodulated[ x ] = demodulate( samples[ index ], + samples[ index + 1 ] ); + } + + mListener.receive( new RealBuffer( demodulated ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } + + @Override + public void dispose() + { + mListener = null; + } +} diff --git a/src/dsp/nbfm/FMDiscriminator.java b/src/dsp/fm/FMDiscriminator.java similarity index 90% rename from src/dsp/nbfm/FMDiscriminator.java rename to src/dsp/fm/FMDiscriminator.java index 2fc6d8ce..3e32d2d2 100644 --- a/src/dsp/nbfm/FMDiscriminator.java +++ b/src/dsp/fm/FMDiscriminator.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,16 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.nbfm; +package dsp.fm; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealSampleListener; -public class FMDiscriminator implements Listener +@Deprecated +public class FMDiscriminator implements Listener { private RealSampleListener mListener; - private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f ); + private Complex mPreviousSample = new Complex( 0.0f, 0.0f ); private double mGain; /** @@ -51,7 +52,7 @@ public class FMDiscriminator implements Listener } @Override - public void receive( ComplexSample currentSample ) + public void receive( Complex currentSample ) { /** * Multiply the current sample against the complex conjugate of the diff --git a/src/dsp/nbfm/FilteringNBFMDemodulator.java b/src/dsp/fm/FilteringNBFMDemodulator.java similarity index 87% rename from src/dsp/nbfm/FilteringNBFMDemodulator.java rename to src/dsp/fm/FilteringNBFMDemodulator.java index 5644c1be..391d4938 100644 --- a/src/dsp/nbfm/FilteringNBFMDemodulator.java +++ b/src/dsp/fm/FilteringNBFMDemodulator.java @@ -15,34 +15,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.nbfm; +package dsp.fm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealSampleBroadcaster; import sample.real.RealSampleListener; -import dsp.filter.ComplexFIRFilter; +import dsp.filter.ComplexFIRFilterOld; import dsp.filter.FilterFactory; import dsp.filter.FloatFIRFilter; import dsp.filter.Window.WindowType; -public class FilteringNBFMDemodulator implements Listener +@Deprecated +public class FilteringNBFMDemodulator implements Listener { private final static Logger mLog = LoggerFactory.getLogger( FilteringNBFMDemodulator.class ); private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); - private ComplexFIRFilter mIQFilter; + private ComplexFIRFilterOld mIQFilter; private FloatFIRFilter mAudioFilter; private FMDiscriminator mDiscriminator; - public FilteringNBFMDemodulator( double[] iqFilter, double iqGain, - double[] audioFilter, double audioGain ) + public FilteringNBFMDemodulator( float[] iqFilter, float iqGain, + float[] audioFilter, float audioGain ) { - mIQFilter = new ComplexFIRFilter( iqFilter, iqGain ); + mIQFilter = new ComplexFIRFilterOld( iqFilter, iqGain ); mAudioFilter = new FloatFIRFilter( audioFilter, audioGain ); @@ -62,7 +63,7 @@ public class FilteringNBFMDemodulator implements Listener public FilteringNBFMDemodulator() { this( FilterFactory.getLowPass( 48000, 5000, 7000, 48, WindowType.HAMMING, true ), - 1.0002, + 1.0002f, FilterFactory.getLowPass( 48000, 3000, 6000, 48, WindowType.HAMMING, true ), 2 ); } @@ -72,7 +73,7 @@ public class FilteringNBFMDemodulator implements Listener * processed */ @Override - public void receive( ComplexSample quadratureSample ) + public void receive( Complex quadratureSample ) { mIQFilter.receive( quadratureSample ); } diff --git a/src/dsp/nbfm/NBFMDemodulator.java b/src/dsp/fm/NBFMDemodulator.java similarity index 85% rename from src/dsp/nbfm/NBFMDemodulator.java rename to src/dsp/fm/NBFMDemodulator.java index 94e78f20..ef0e33e9 100644 --- a/src/dsp/nbfm/NBFMDemodulator.java +++ b/src/dsp/fm/NBFMDemodulator.java @@ -15,20 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.nbfm; +package dsp.fm; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealSampleBroadcaster; import sample.real.RealSampleListener; -import dsp.filter.ComplexFIRFilter; +import dsp.filter.ComplexFIRFilterOld; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; -public class NBFMDemodulator implements Listener +@Deprecated +public class NBFMDemodulator implements Listener { private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); - private ComplexFIRFilter mIQFilter; + private ComplexFIRFilterOld mIQFilter; private FMDiscriminator mDiscriminator; /** @@ -38,12 +39,12 @@ public class NBFMDemodulator implements Listener public NBFMDemodulator() { this( FilterFactory.getLowPass( 48000, 6250, 73, WindowType.HAMMING ), - 1.0002, true ); + 1.0002f, true ); } - public NBFMDemodulator( double[] iqFilter, double iqGain, boolean afc ) + public NBFMDemodulator( float[] iqFilter, float iqGain, boolean afc ) { - mIQFilter = new ComplexFIRFilter( iqFilter, iqGain ); + mIQFilter = new ComplexFIRFilterOld( iqFilter, iqGain ); mDiscriminator = new FMDiscriminator( 1 ); @@ -66,7 +67,7 @@ public class NBFMDemodulator implements Listener * processed */ @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mIQFilter.receive( sample ); } diff --git a/src/dsp/fsk/FSK2Decoder.java b/src/dsp/fsk/FSK2Decoder.java index 99a5e392..13646d06 100644 --- a/src/dsp/fsk/FSK2Decoder.java +++ b/src/dsp/fsk/FSK2Decoder.java @@ -26,7 +26,7 @@ import java.util.BitSet; import java.util.List; import sample.Listener; -import sample.real.RealSampleListener; +import sample.real.RealBuffer; import buffer.BooleanAveragingBuffer; import dsp.symbol.SymbolEvent; import dsp.symbol.SymbolEvent.Shift; @@ -51,7 +51,7 @@ import dsp.symbol.SymbolEvent.Shift; * Implements instrumentable interface, so that slice events can be received * externally to analyze decoder performance. */ -public class FSK2Decoder implements Instrumentable, RealSampleListener +public class FSK2Decoder implements Instrumentable, Listener { public enum Output{ NORMAL, INVERTED }; @@ -95,23 +95,26 @@ public class FSK2Decoder implements Instrumentable, RealSampleListener * Primary sample input */ @Override - public void receive( float floatSample ) + public void receive( RealBuffer buffer ) { - /* Square the sample. Greater than zero is a 1 (true) and less than - * zero is a 0 (false) */ - boolean bitSample = ( floatSample >= 0.0f ); + for( float sample: buffer.getSamples() ) + { + /* Square the sample. Greater than zero is a 1 (true) and less than + * zero is a 0 (false) */ + boolean bitSample = ( sample >= 0.0f ); - /* Feed the delay buffer and fetch the one-baud delayed sample */ - boolean delayedBitSample = mDelayBuffer.get( bitSample ); + /* Feed the delay buffer and fetch the one-baud delayed sample */ + boolean delayedBitSample = mDelayBuffer.get( bitSample ); - /* Correlation: xor current bit with delayed bit */ - boolean softBit = bitSample ^ delayedBitSample; + /* Correlation: xor current bit with delayed bit */ + boolean softBit = bitSample ^ delayedBitSample; - /* Low pass filter to smooth the correlated values */ - boolean filteredSoftBit = mLowPassFilter.getAverage( softBit ); + /* Low pass filter to smooth the correlated values */ + boolean filteredSoftBit = mLowPassFilter.getAverage( softBit ); - /* Send the filtered correlated bit to the slicer */ - mSlicer.receive( filteredSoftBit ); + /* Send the filtered correlated bit to the slicer */ + mSlicer.receive( filteredSoftBit ); + } } /** diff --git a/src/dsp/fsk/LTRFSKDecoder.java b/src/dsp/fsk/LTRFSKDecoder.java index 9a81180c..089be152 100644 --- a/src/dsp/fsk/LTRFSKDecoder.java +++ b/src/dsp/fsk/LTRFSKDecoder.java @@ -27,17 +27,17 @@ import java.util.ArrayList; import java.util.List; import sample.Listener; -import sample.real.RealSampleListener; -import dsp.filter.DCRemovalFilter3; +import sample.real.RealBuffer; import dsp.filter.Filters; import dsp.filter.FloatHalfBandFilter; import dsp.filter.FloatHalfBandNoDecimateFilter; import dsp.filter.LTRPulseShapingFilter; import dsp.filter.SquaringFilter; +import dsp.filter.dc.IIRSinglePoleDCRemovalFilter; import dsp.symbol.Slicer; import dsp.symbol.Slicer.Output; -public class LTRFSKDecoder implements RealSampleListener, Instrumentable +public class LTRFSKDecoder implements Listener, Instrumentable { private FloatHalfBandFilter mHBFilter1; private FloatHalfBandFilter mHBFilter2; @@ -45,7 +45,7 @@ public class LTRFSKDecoder implements RealSampleListener, Instrumentable private FloatHalfBandFilter mHBFilter4; private FloatHalfBandFilter mHBFilter5; private FloatHalfBandNoDecimateFilter mHBFilter6; - private DCRemovalFilter3 mDCFilter; + private IIRSinglePoleDCRemovalFilter mDCFilter; private SquaringFilter mSquaringFilter; private LTRPulseShapingFilter mPulseShaper; private Slicer mSlicer; @@ -69,29 +69,29 @@ public class LTRFSKDecoder implements RealSampleListener, Instrumentable public LTRFSKDecoder() { mHBFilter1 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter2 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter1.setListener( mHBFilter2 ); mHBFilter3 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter2.setListener( mHBFilter3 ); mHBFilter4 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter3.setListener( mHBFilter4 ); mHBFilter5 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter4.setListener( mHBFilter5 ); mHBFilter6 = new FloatHalfBandNoDecimateFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter5.setListener( mHBFilter6 ); - mDCFilter = new DCRemovalFilter3( 0.9946f ); + mDCFilter = new IIRSinglePoleDCRemovalFilter( 0.9946f ); mHBFilter6.setListener( mDCFilter ); mSquaringFilter = new SquaringFilter(); @@ -105,9 +105,12 @@ public class LTRFSKDecoder implements RealSampleListener, Instrumentable } @Override - public void receive( float sample ) + public void receive( RealBuffer buffer ) { - mHBFilter1.receive( sample ); + for( float sample: buffer.getSamples() ) + { + mHBFilter1.receive( sample ); + } } public void addListener( Listener listener ) diff --git a/src/dsp/gain/RealAutomaticGainControl.java b/src/dsp/gain/AutomaticGainControl.java similarity index 88% rename from src/dsp/gain/RealAutomaticGainControl.java rename to src/dsp/gain/AutomaticGainControl.java index 53e98d90..f7afe8da 100644 --- a/src/dsp/gain/RealAutomaticGainControl.java +++ b/src/dsp/gain/AutomaticGainControl.java @@ -2,9 +2,6 @@ package dsp.gain; import java.util.concurrent.atomic.AtomicBoolean; -import sample.real.RealBuffer; -import sample.real.RealSampleListener; -import sample.real.RealSampleProvider; import buffer.DoubleCircularBuffer; import buffer.FloatCircularBuffer; @@ -61,8 +58,7 @@ import buffer.FloatCircularBuffer; * those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of Moe Wheatley. ******************************************************************************/ -public class RealAutomaticGainControl implements RealSampleListener, - RealSampleProvider +public class AutomaticGainControl { private static final double SAMPLE_RATE = 48000; @@ -139,38 +135,10 @@ public class RealAutomaticGainControl implements RealSampleListener, private DoubleCircularBuffer mMagnitudeBuffer = new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) ); - private RealSampleListener mListener; - - public RealAutomaticGainControl() + public AutomaticGainControl() { } - @Override - public void receive( float currentSample ) - { - float processed = process( currentSample ); - - if( mListener != null ) - { - mListener.receive( processed ); - } - } - - /** - * Applies AGC to a buffer of real (float) samples - */ - public RealBuffer process( RealBuffer buffer ) - { - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x++ ) - { - samples[ x ] = process( samples[ x ] ); - } - - return new RealBuffer( samples ); - } - public float process( float currentSample ) { float delayedSample = mDelayBuffer.get( currentSample ); @@ -233,9 +201,7 @@ public class RealAutomaticGainControl implements RealSampleListener, } - delayedSample = (float)( delayedSample * gain ); - - return delayedSample; + return (float)( delayedSample * gain ); } /** @@ -254,16 +220,4 @@ public class RealAutomaticGainControl implements RealSampleListener, { return mAGCEnabled.get(); } - - @Override - public void setListener( RealSampleListener listener ) - { - mListener = listener; - } - - @Override - public void removeListener( RealSampleListener listener ) - { - mListener = null; - } } diff --git a/src/dsp/gain/AutomaticGainControl_RB.java b/src/dsp/gain/AutomaticGainControl_RB.java new file mode 100644 index 00000000..e96c600a --- /dev/null +++ b/src/dsp/gain/AutomaticGainControl_RB.java @@ -0,0 +1,53 @@ +package dsp.gain; + +import sample.Listener; +import sample.real.RealBuffer; + +public class AutomaticGainControl_RB extends AutomaticGainControl + implements Listener +{ + private Listener mListener; + + public AutomaticGainControl_RB() + { + } + + public void dispose() + { + mListener = null; + } + + /** + * Applies AGC to a buffer of real (float) samples + */ + public RealBuffer process( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = process( samples[ x ] ); + } + + return buffer; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( process( buffer ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/gain/ComplexAutomaticGainControl.java b/src/dsp/gain/ComplexAutomaticGainControl.java index 9fef2ace..4ecbb2ad 100644 --- a/src/dsp/gain/ComplexAutomaticGainControl.java +++ b/src/dsp/gain/ComplexAutomaticGainControl.java @@ -4,7 +4,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import sample.Listener; import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; import buffer.ComplexCircularBuffer; import buffer.DoubleCircularBuffer; @@ -61,8 +61,8 @@ import buffer.DoubleCircularBuffer; * those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of Moe Wheatley. ******************************************************************************/ -public class ComplexAutomaticGainControl implements Listener, - Provider +public class ComplexAutomaticGainControl implements Listener, + Provider { private static final double SAMPLE_RATE = 48000; @@ -138,16 +138,16 @@ public class ComplexAutomaticGainControl implements Listener, private DoubleCircularBuffer mMagnitudeBuffer = new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) ); - private Listener mListener; + private Listener mListener; public ComplexAutomaticGainControl() { } @Override - public void receive( ComplexSample currentSample ) + public void receive( Complex currentSample ) { - ComplexSample delayedSample = mDelayBuffer.get( currentSample ); + Complex delayedSample = mDelayBuffer.get( currentSample ); double gain = MANUAL_AGC_GAIN; @@ -235,13 +235,13 @@ public class ComplexAutomaticGainControl implements Listener, } @Override - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } @Override - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/dsp/gain/ComplexFeedForwardGainControl.java b/src/dsp/gain/ComplexFeedForwardGainControl.java index 674b038e..89d3f057 100644 --- a/src/dsp/gain/ComplexFeedForwardGainControl.java +++ b/src/dsp/gain/ComplexFeedForwardGainControl.java @@ -4,8 +4,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; import buffer.FloatCircularBuffer; /******************************************************************************* @@ -31,8 +31,7 @@ import buffer.FloatCircularBuffer; * feedforward_agc_cc_impl.cc * ******************************************************************************/ -public class ComplexFeedForwardGainControl - implements Listener, Provider +public class ComplexFeedForwardGainControl implements ComplexSampleListener { private final static Logger mLog = LoggerFactory.getLogger( ComplexFeedForwardGainControl.class ); @@ -40,7 +39,7 @@ public class ComplexFeedForwardGainControl public static final float OBJECTIVE_ENVELOPE = 1.0f; public static final float MINIMUM_ENVELOPE = 0.0001f; - private Listener mListener; + private ComplexSampleListener mListener; private FloatCircularBuffer mEnvelopeHistory; @@ -65,9 +64,9 @@ public class ComplexFeedForwardGainControl } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { - float envelope = sample.envelope(); + float envelope = Complex.envelope( inphase, quadrature ); if( envelope > mMaxEnvelope ) { @@ -99,9 +98,7 @@ public class ComplexFeedForwardGainControl /* Apply current gain value to the sample and send to the listener */ if( mListener != null ) { - sample.multiply( mGain ); - - mListener.receive( sample ); + mListener.receive( inphase *= mGain, quadrature *= mGain ); } } @@ -110,15 +107,8 @@ public class ComplexFeedForwardGainControl mGain = OBJECTIVE_ENVELOPE / mMaxEnvelope; } - @Override - public void setListener( Listener listener ) + public void setListener( ComplexSampleListener listener ) { mListener = listener; } - - @Override - public void removeListener( Listener listener ) - { - mListener = null; - } } diff --git a/src/dsp/gain/ComplexGain.java b/src/dsp/gain/ComplexGain.java index a8ea1b4e..e86e0048 100644 --- a/src/dsp/gain/ComplexGain.java +++ b/src/dsp/gain/ComplexGain.java @@ -1,12 +1,12 @@ package dsp.gain; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ComplexGain implements Listener +public class ComplexGain implements Listener { private float mGain; - private Listener mListener; + private Listener mListener; public ComplexGain( float gain ) { @@ -14,7 +14,7 @@ public class ComplexGain implements Listener } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { if( mListener != null ) { @@ -24,7 +24,7 @@ public class ComplexGain implements Listener } } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } diff --git a/src/dsp/psk/CQPSKDemodulator.java b/src/dsp/psk/CQPSKDemodulator.java index 94aa97de..c968c685 100644 --- a/src/dsp/psk/CQPSKDemodulator.java +++ b/src/dsp/psk/CQPSKDemodulator.java @@ -35,7 +35,6 @@ import instrument.tap.Tap; import instrument.tap.stream.EyeDiagramData; import instrument.tap.stream.EyeDiagramDataTap; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,24 +43,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; import source.tuner.frequency.FrequencyChangeListener; import buffer.FloatAveragingBuffer; -import dsp.filter.interpolator.QPSKInterpolator; +import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; /** - * Implements a CQPSK demodulator using a Gardner Detector to determine optimal - * sampling timing and a Costas Loop as a phase locked loop synchronized with - * the incoming signal carrier frequency. + * Implements a LSM (CQPSK) demodulator using a Gardner Detector to determine + * optimal sampling timing and a Costas Loop as a phase locked loop synchronized + * with the incoming signal carrier frequency. * * Sample Rate: 48000 * Symbol Rate: 4800 */ -public class CQPSKDemodulator implements Instrumentable, - Listener, - Provider +public class CQPSKDemodulator implements Instrumentable, ComplexSampleListener { private final static Logger mLog = LoggerFactory.getLogger( CQPSKDemodulator.class ); @@ -70,12 +67,10 @@ public class CQPSKDemodulator implements Instrumentable, public static final float THETA = (float)( Math.PI / 4.0d ); /* 45 degree point */ - public static final ComplexSample POINT_45_DEGREES = - new ComplexSample( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); + public static final Complex POINT_45_DEGREES = + new Complex( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); - private DecimalFormat mDecimalFormat = new DecimalFormat( "0.000000" ); - - private Listener mListener; + private Listener mSymbolListener; private GardnerSymbolTiming mGardnerDetector = new GardnerSymbolTiming(); @@ -89,10 +84,20 @@ public class CQPSKDemodulator implements Instrumentable, { } - @Override - public void receive( ComplexSample sample ) + public void setSymbolListener( Listener listener ) { - mGardnerDetector.receive( sample ); + mSymbolListener = listener; + } + + public void removeSymbolListener() + { + mSymbolListener = null; + } + + @Override + public void receive( float inphase, float quadrature ) + { + mGardnerDetector.receive( inphase, quadrature ); } /** @@ -211,14 +216,15 @@ public class CQPSKDemodulator implements Instrumentable, /** * Gardner symbol timing detector */ - public class GardnerSymbolTiming implements Listener + public class GardnerSymbolTiming implements ComplexSampleListener { public static final int HALF_SAMPLES_PER_SYMBOL = 5; public static final int SAMPLES_PER_SYMBOL = 10; public static final int TWICE_SAMPLES_PER_SYMBOL = 20; - - private ComplexSample[] mDelayLine = - new ComplexSample[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + + private float[] mDelayLineInphase = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private float[] mDelayLineQuadrature = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private int mDelayLinePointer = 0; /* Sampling point */ @@ -231,25 +237,21 @@ public class CQPSKDemodulator implements Instrumentable, private float mOmegaRel = 0.005f; private float mOmegaMid = 10.0f; - private QPSKInterpolator mInterpolator = new QPSKInterpolator( 1.0f ); + private RealInterpolator mInterpolator = new RealInterpolator( 1.0f ); + + private Complex mPreviousSample; + private Complex mPreviousMiddleSample; + private Complex mPreviousSymbol; - private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f ); - private ComplexSample mPreviousMiddleSample = new ComplexSample( 0.0f, 0.0f ); - private ComplexSample mPreviousSymbol = new ComplexSample( 0.0f, 0.0f ); - /** * Provides symbol sampling timing control */ public GardnerSymbolTiming() { - for( int x = 0; x < ( 2 * TWICE_SAMPLES_PER_SYMBOL ); x++ ) - { - mDelayLine[ x ] = new ComplexSample( 0.0f, 0.0f ); - } } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { /* Count down samples per symbol until we calculate the symbol */ mMu--; @@ -258,14 +260,20 @@ public class CQPSKDemodulator implements Instrumentable, /* Mix incoming sample with costas loop to remove any rotation * that is present from a mis-tuned carrier frequency */ - ComplexSample derotatedSample = ComplexSample - .multiply( mCostasLoop.getCurrentVector(), sample ); - + + Complex costas = mCostasLoop.getCurrentVector(); + + float derotatedInphase = Complex.multiplyInphase( inphase, quadrature, costas.inphase(), costas.quadrature() ); + float derotatedQuadrature = Complex.multiplyQuadrature( inphase, quadrature, costas.inphase(), costas.quadrature() ); + /* Fill up the delay line to use with the interpolator */ - mDelayLine[ mDelayLinePointer ] = derotatedSample; - mDelayLine[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample; + mDelayLineInphase[ mDelayLinePointer ] = derotatedInphase; + mDelayLineInphase[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedInphase; - /* Imcrement pointer and keep pointer in bounds */ + mDelayLineQuadrature[ mDelayLinePointer ] = derotatedQuadrature; + mDelayLineQuadrature[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedQuadrature; + + /* Increment pointer and keep pointer in bounds */ mDelayLinePointer = ( mDelayLinePointer + 1 ) % TWICE_SAMPLES_PER_SYMBOL; /* Calculate the symbol once we've stored enough samples */ @@ -282,19 +290,24 @@ public class CQPSKDemodulator implements Instrumentable, } /* Calculate interpolated middle sample and current sample */ - ComplexSample middleSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer, mMu ); - - ComplexSample currentSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer + half_sps, half_mu ); - - /* Multiply current and previous samples to get symbols to use - * for gardner error feedback */ - ComplexSample middleSymbol = ComplexSample.multiply( - middleSample, mPreviousMiddleSample.conjugate() ); + float middleSampleInphase = mInterpolator.filter( + mDelayLineInphase, mDelayLinePointer, mMu ); + float middleSampleQuadrature = mInterpolator.filter( + mDelayLineQuadrature, mDelayLinePointer, mMu ); - ComplexSample currentSymbol = ComplexSample.multiply( - currentSample, mPreviousSample.conjugate() ); + int index = mDelayLinePointer + half_sps; + + float currentSampleInphase = mInterpolator.filter( + mDelayLineInphase, index, half_mu ); + float currentSampleQuadrature = mInterpolator.filter( + mDelayLineQuadrature, index, half_mu ); + + /* Multiply current sample and conjugate (negated quadrature) of + * previous sample to get symbols to use for gardner error feedback */ + Complex middleSymbol = Complex.multiply( middleSampleInphase, + middleSampleQuadrature, mPreviousMiddleSample.conjugate() ); + Complex currentSymbol = Complex.multiply( currentSampleInphase, + currentSampleQuadrature, mPreviousSample.conjugate() ); /* Set gain to unity */ middleSymbol.normalize(); @@ -312,8 +325,10 @@ public class CQPSKDemodulator implements Instrumentable, if( mEyeDiagramDataTap != null ) { mEyeDiagramDataTap.receive( - new EyeDiagramData( Arrays.copyOfRange( mDelayLine, - 0, 20 ), mMu, (float)half_sps + half_mu, gardnerError ) ); + new EyeDiagramData( + Arrays.copyOfRange( mDelayLineInphase, 0, 20 ), + Arrays.copyOfRange( mDelayLineQuadrature, 0, 20 ), + mMu, (float)half_sps + half_mu, gardnerError ) ); } /* mOmega is samples per symbol and is constrained to floating @@ -325,8 +340,8 @@ public class CQPSKDemodulator implements Instrumentable, mMu += mOmega + ( mGainMu * gardnerError ); /* Store current samples/symbols to use for the next period */ - mPreviousSample = currentSample; - mPreviousMiddleSample = middleSample; + mPreviousSample.setValues( currentSampleInphase, currentSampleQuadrature ); + mPreviousMiddleSample.setValues( middleSampleInphase, middleSampleQuadrature ); mPreviousSymbol = currentSymbol; /* Update costas loop using phase error present in current @@ -337,9 +352,9 @@ public class CQPSKDemodulator implements Instrumentable, // ComplexSample.multiply( currentSymbol, POINT_45_DEGREES ) ); /* Dispatch the differentiated symbol to the registered listener */ - if( mListener != null ) + if( mSymbolListener != null ) { - mListener.receive( currentSymbol ); + mSymbolListener.receive( currentSymbol ); } } } @@ -356,7 +371,7 @@ public class CQPSKDemodulator implements Instrumentable, * gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's * OP25/cqpsk.py */ - public class CostasLoop implements Listener + public class CostasLoop implements Listener { public static final double TWO_PI = 2.0 * Math.PI; @@ -442,19 +457,19 @@ public class CQPSKDemodulator implements Instrumentable, /** * Current vector of the loop rotated by THETA degrees. */ - public ComplexSample getCurrentVector() + public Complex getCurrentVector() { // return ComplexSample.fromAngle( mLoopPhase + THETA ); - return ComplexSample.fromAngle( mLoopPhase ); + return Complex.fromAngle( mLoopPhase ); } @Override - public void receive( ComplexSample sample ) + public void receive( Complex complex ) { /* Calculate phase error */ - float phaseError = getPhaseError( sample ); + float phaseError = getPhaseError( complex ); - Dibit d = QPSKPolarSlicer.decide( sample ); + Dibit d = QPSKPolarSlicer.decide( complex ); // mLog.debug( "Dibit: " + d.name() + " Error: " + phaseError ); /* Adjust for phase error */ @@ -503,30 +518,30 @@ public class CQPSKDemodulator implements Instrumentable, * with the carrier frequency. Provides error feedback to adjust * mixer frequency. */ - public float getPhaseError( ComplexSample sample ) + public float getPhaseError( Complex complex ) { float phase_error = 0; - if( Math.abs( sample.real() ) > Math.abs( sample.imaginary() ) ) + if( Math.abs( complex.inphase() ) > Math.abs( complex.quadrature() ) ) { - if( sample.real() > 0 ) + if( complex.inphase() > 0 ) { - phase_error = -sample.imaginary(); + phase_error = -complex.quadrature(); } else { - phase_error = sample.imaginary(); + phase_error = complex.quadrature(); } } else { - if( sample.imaginary() > 0 ) + if( complex.quadrature() > 0 ) { - phase_error = sample.real(); + phase_error = complex.inphase(); } else { - phase_error = -sample.real(); + phase_error = -complex.inphase(); } } @@ -534,18 +549,6 @@ public class CQPSKDemodulator implements Instrumentable, } } - @Override - public void setListener( Listener listener ) - { - mListener = listener; - } - - @Override - public void removeListener( Listener listener ) - { - mListener = null; - } - @Override public List getTaps() { diff --git a/src/dsp/psk/CQPSKDemodulator2.java b/src/dsp/psk/CQPSKDemodulator2.java index 1b0fe271..def7d62c 100644 --- a/src/dsp/psk/CQPSKDemodulator2.java +++ b/src/dsp/psk/CQPSKDemodulator2.java @@ -45,10 +45,11 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; import source.tuner.frequency.FrequencyChangeListener; import buffer.FloatAveragingBuffer; -import dsp.filter.interpolator.QPSKInterpolator; +import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; /** @@ -59,9 +60,8 @@ import dsp.symbol.Dibit; * Sample Rate: 48000 * Symbol Rate: 4800 */ -public class CQPSKDemodulator2 implements Instrumentable, - Listener, - Provider +public class CQPSKDemodulator2 implements Instrumentable, ComplexSampleListener, + Provider { private final static Logger mLog = LoggerFactory.getLogger( CQPSKDemodulator2.class ); @@ -70,12 +70,12 @@ public class CQPSKDemodulator2 implements Instrumentable, public static final float THETA = (float)( Math.PI / 4.0d ); /* 45 degree point */ - public static final ComplexSample POINT_45_DEGREES = - new ComplexSample( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); + public static final Complex POINT_45_DEGREES = + new Complex( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); private DecimalFormat mDecimalFormat = new DecimalFormat( "0.000000" ); - private Listener mListener; + private Listener mListener; private EarlyLateSymbolTiming mGardnerDetector = new EarlyLateSymbolTiming(); @@ -90,9 +90,9 @@ public class CQPSKDemodulator2 implements Instrumentable, } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { - mGardnerDetector.receive( sample ); + mGardnerDetector.receive( inphase, quadrature ); } /** @@ -218,13 +218,14 @@ public class CQPSKDemodulator2 implements Instrumentable, * attempting to find the largest magnitude value between two interpolated * sample points during each symbol period. */ - public class EarlyLateSymbolTiming implements Listener + public class EarlyLateSymbolTiming implements ComplexSampleListener { public static final int SAMPLES_PER_SYMBOL = 10; public static final int TWICE_SAMPLES_PER_SYMBOL = 20; - private ComplexSample[] mDelayLine = - new ComplexSample[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private float[] mDelayLineInphase = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private float[] mDelayLineQuadrature = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private int mDelayLinePointer = 0; /* Sampling point */ @@ -237,23 +238,19 @@ public class CQPSKDemodulator2 implements Instrumentable, private float mOmegaRel = 0.005f; private float mOmegaMid = 10.0f; - private QPSKInterpolator mInterpolator = new QPSKInterpolator( 1.0f ); + private RealInterpolator mInterpolator = new RealInterpolator( 1.0f ); - private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f ); + private Complex mPreviousSample = new Complex( 0.0f, 0.0f ); /** * Provides symbol sampling timing control */ public EarlyLateSymbolTiming() { - for( int x = 0; x < ( 2 * TWICE_SAMPLES_PER_SYMBOL ); x++ ) - { - mDelayLine[ x ] = new ComplexSample( 0.0f, 0.0f ); - } } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { /* Count down samples per symbol until we calculate the symbol */ mMu--; @@ -262,29 +259,31 @@ public class CQPSKDemodulator2 implements Instrumentable, /* Mix incoming sample with costas loop to remove any rotation * that is present from a mis-tuned carrier frequency */ - ComplexSample derotatedSample = ComplexSample - .multiply( mCostasLoop.getCurrentVector(), sample ); + Complex derotatedSample = Complex.multiply( + mCostasLoop.getCurrentVector(), inphase, quadrature ); /* Fill up the delay line to use with the interpolator */ - mDelayLine[ mDelayLinePointer ] = derotatedSample; - mDelayLine[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample; - + mDelayLineInphase[ mDelayLinePointer ] = derotatedSample.inphase(); + mDelayLineInphase[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample.inphase(); + mDelayLineQuadrature[ mDelayLinePointer ] = derotatedSample.quadrature(); + mDelayLineQuadrature[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample.quadrature(); + /* Imcrement pointer and keep pointer in bounds */ mDelayLinePointer = ( mDelayLinePointer + 1 ) % TWICE_SAMPLES_PER_SYMBOL; /* Calculate the symbol once we've stored enough samples */ if( mMu <= 1.0f ) { - ComplexSample earlySample = mInterpolator - .filter( mDelayLine, mDelayLinePointer, mMu ); + Complex earlySample = mInterpolator.filter( mDelayLineInphase, + mDelayLineQuadrature, mDelayLinePointer, mMu ); - ComplexSample middleSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer + 1, mMu ); + Complex middleSample = mInterpolator.filter( mDelayLineInphase, + mDelayLineQuadrature, mDelayLinePointer + 1, mMu ); - ComplexSample lateSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer + 2, mMu ); + Complex lateSample = mInterpolator.filter( mDelayLineInphase, + mDelayLineQuadrature, mDelayLinePointer + 2, mMu ); - ComplexSample currentSymbol = ComplexSample.multiply( + Complex currentSymbol = Complex.multiply( middleSample, mPreviousSample.conjugate() ); /* Set symbol gain to unity */ @@ -295,10 +294,12 @@ public class CQPSKDemodulator2 implements Instrumentable, if( mEyeDiagramDataTap != null ) { - mEyeDiagramDataTap.receive( - new EyeDiagramData( Arrays.copyOfRange( mDelayLine, 0, 20 ), - (float)mDelayLinePointer + 4.0f + mMu, - (float)mDelayLinePointer + 4.0f + mMu + 0.25f, earlyLateError ) ); + mEyeDiagramDataTap.receive( new EyeDiagramData( + Arrays.copyOfRange( mDelayLineInphase, 0, 20 ), + Arrays.copyOfRange( mDelayLineQuadrature, 0, 20 ), + (float)mDelayLinePointer + 4.0f + mMu, + (float)mDelayLinePointer + 4.0f + mMu + 0.25f, + earlyLateError ) ); } /* mOmega is samples per symbol and is constrained to floating @@ -341,7 +342,7 @@ public class CQPSKDemodulator2 implements Instrumentable, * gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's * OP25/cqpsk.py */ - public class CostasLoop implements Listener + public class CostasLoop implements Listener { public static final double TWO_PI = 2.0 * Math.PI; @@ -427,14 +428,14 @@ public class CQPSKDemodulator2 implements Instrumentable, /** * Current vector of the loop rotated by THETA degrees. */ - public ComplexSample getCurrentVector() + public Complex getCurrentVector() { // return ComplexSample.fromAngle( mLoopPhase + THETA ); - return ComplexSample.fromAngle( mLoopPhase ); + return Complex.fromAngle( mLoopPhase ); } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { /* Calculate phase error */ float phaseError = getPhaseError( sample ); @@ -488,7 +489,7 @@ public class CQPSKDemodulator2 implements Instrumentable, * with the carrier frequency. Provides error feedback to adjust * mixer frequency. */ - public float getPhaseError( ComplexSample sample ) + public float getPhaseError( Complex sample ) { float phase_error = 0; @@ -520,13 +521,13 @@ public class CQPSKDemodulator2 implements Instrumentable, } @Override - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } @Override - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/dsp/psk/QPSKPolarSlicer.java b/src/dsp/psk/QPSKPolarSlicer.java index 73537a30..db6469e6 100644 --- a/src/dsp/psk/QPSKPolarSlicer.java +++ b/src/dsp/psk/QPSKPolarSlicer.java @@ -2,10 +2,27 @@ package dsp.psk; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import dsp.symbol.Dibit; -public class QPSKPolarSlicer implements Listener +/******************************************************************************* + * 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 + ******************************************************************************/ +public class QPSKPolarSlicer implements Listener { private Broadcaster mBroadcaster = new Broadcaster(); @@ -15,7 +32,7 @@ public class QPSKPolarSlicer implements Listener * * \ 00 / * \ / - * 11 \/ 10 + * 01 \/ 10 * /\ * / \ * / 11 \ @@ -41,16 +58,16 @@ public class QPSKPolarSlicer implements Listener } @Override - public void receive( ComplexSample sample ) + public void receive( Complex complex ) { - mBroadcaster.broadcast( decide( sample ) ); + mBroadcaster.broadcast( decide( complex ) ); } - public static Dibit decide( ComplexSample sample ) + public static Dibit decide( Complex complex ) { - if( sample.inPhaseAbsolute() > sample.quadratureAbsolute() ) + if( Math.abs( complex.inphase() ) > Math.abs( complex.quadrature() ) ) { - if( sample.inphase() > 0 ) + if( complex.inphase() > 0 ) { return Dibit.D10_MINUS_1; } @@ -61,7 +78,7 @@ public class QPSKPolarSlicer implements Listener } else { - if( sample.quadrature() > 0 ) + if( complex.quadrature() > 0 ) { return Dibit.D00_PLUS_1; } diff --git a/src/dsp/psk/QPSKStarSlicer.java b/src/dsp/psk/QPSKStarSlicer.java index 44b80fcf..7f16f38d 100644 --- a/src/dsp/psk/QPSKStarSlicer.java +++ b/src/dsp/psk/QPSKStarSlicer.java @@ -2,10 +2,10 @@ package dsp.psk; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import dsp.symbol.Dibit; -public class QPSKStarSlicer implements Listener +public class QPSKStarSlicer implements Listener { private Broadcaster mBroadcaster = new Broadcaster(); @@ -38,12 +38,12 @@ public class QPSKStarSlicer implements Listener } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBroadcaster.broadcast( decide( sample ) ); } - public static Dibit decide( ComplexSample sample ) + public static Dibit decide( Complex sample ) { if( sample.inphase() > 0 ) { diff --git a/src/eventlog/CallEventLogger.java b/src/eventlog/CallEventLogger.java index a2195090..71363dbe 100644 --- a/src/eventlog/CallEventLogger.java +++ b/src/eventlog/CallEventLogger.java @@ -19,8 +19,8 @@ package eventlog; import java.nio.file.Path; +import module.decode.event.CallEvent; import sample.Listener; -import controller.activity.CallEvent; public class CallEventLogger extends EventLogger implements Listener { diff --git a/src/eventlog/EventLogManager.java b/src/eventlog/EventLogManager.java index 2db0b41b..d67607f0 100644 --- a/src/eventlog/EventLogManager.java +++ b/src/eventlog/EventLogManager.java @@ -19,8 +19,8 @@ package eventlog; import java.nio.file.Path; +import module.ProcessingChainOld; import properties.SystemProperties; -import controller.channel.ProcessingChain; import eventlog.MessageEventLogger.Type; public class EventLogManager @@ -33,7 +33,7 @@ public class EventLogManager .getApplicationFolder( "event_logs" ); } - public EventLogger getLogger( ProcessingChain chain, EventLogType eventLogType ) + public EventLogger getLogger( ProcessingChainOld chain, EventLogType eventLogType ) { StringBuilder sb = new StringBuilder(); diff --git a/src/filter/FilterSet.java b/src/filter/FilterSet.java index eba21694..10823aea 100644 --- a/src/filter/FilterSet.java +++ b/src/filter/FilterSet.java @@ -14,6 +14,11 @@ public class FilterSet implements IFilter mName = name; } + public FilterSet() + { + mName = "Message Filter"; + } + public String getName() { return mName; diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 57aacf78..d4104990 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -73,9 +73,6 @@ public class SDRTrunk public SDRTrunk() { -// LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory(); -// StatusPrinter.print( lc ); - mLog.info( "" ); mLog.info( "" ); mLog.info( "*******************************************************************" ); diff --git a/src/gui/control/ConstellationViewer.java b/src/gui/control/ConstellationViewer.java index 3aa317a7..124b7d47 100644 --- a/src/gui/control/ConstellationViewer.java +++ b/src/gui/control/ConstellationViewer.java @@ -20,11 +20,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import buffer.CircularBuffer; public class ConstellationViewer extends JPanel - implements Listener + implements Listener { private static final long serialVersionUID = 1L; @@ -36,9 +36,9 @@ public class ConstellationViewer extends JPanel private float mSamplesPerSymbol; private float mCounter = 0; private float mOffset = 0; - private CircularBuffer mBuffer = - new CircularBuffer( 5000 ); - private ComplexSample mPrevious = new ComplexSample( 1, 1 ); + private CircularBuffer mBuffer = + new CircularBuffer( 5000 ); + private Complex mPrevious = new Complex( 1, 1 ); public ConstellationViewer( int sampleRate, int symbolRate ) { @@ -76,11 +76,11 @@ public class ConstellationViewer extends JPanel } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer.receive( sample ); - ComplexSample angle = ComplexSample.multiply( sample, mPrevious.conjugate() ); + Complex angle = Complex.multiply( sample, mPrevious.conjugate() ); repaint(); } @@ -109,7 +109,7 @@ public class ConstellationViewer extends JPanel graphics.setColor( Color.BLUE ); - List samples = mBuffer.getElements(); + List samples = mBuffer.getElements(); double centerX = (double)getHeight() / 2.0d; double centerY = (double)getWidth() / 2.0d; @@ -118,7 +118,7 @@ public class ConstellationViewer extends JPanel mCounter = 0; - for( ComplexSample sample: samples ) + for( Complex sample: samples ) { if( mCounter > ( mOffset + mSamplesPerSymbol ) ) { diff --git a/src/instrument/gui/AudioSourceFrame.java b/src/instrument/gui/AudioSourceFrame.java index ff2c0e74..109b64f1 100644 --- a/src/instrument/gui/AudioSourceFrame.java +++ b/src/instrument/gui/AudioSourceFrame.java @@ -34,7 +34,13 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; -import message.MessageDirection; +import module.decode.Decoder; +import module.decode.DecoderType; +import module.decode.fleetsync2.Fleetsync2Decoder; +import module.decode.lj1200.LJ1200Decoder; +import module.decode.mdc1200.MDCDecoder; +import module.decode.p25.P25Decoder.Modulation; +import module.decode.tait.Tait1200Decoder; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -43,21 +49,6 @@ import org.slf4j.LoggerFactory; import source.wave.WaveSource; import source.wave.WaveSource.PositionListener; import controller.ResourceManager; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfigLTRNet; -import decode.config.DecodeConfigMPT1327; -import decode.config.DecodeConfigPassport; -import decode.fleetsync2.Fleetsync2Decoder; -import decode.lj1200.LJ1200Decoder; -import decode.ltrnet.LTRNetDecoder; -import decode.mdc1200.MDCDecoder; -import decode.mpt1327.MPT1327Decoder; -import decode.mpt1327.MPT1327Decoder.Sync; -import decode.p25.P25Decoder; -import decode.p25.P25Decoder.Modulation; -import decode.passport.PassportDecoder; -import decode.tait.Tait1200Decoder; public class AudioSourceFrame extends JInternalFrame implements PositionListener { @@ -188,25 +179,25 @@ public class AudioSourceFrame extends JInternalFrame implements PositionListener decoder = new LJ1200Decoder( null ); break; case LTR_NET: - decoder = new LTRNetDecoder( new DecodeConfigLTRNet(), - mSource.getSampleType(), null, MessageDirection.OSW ); +// decoder = new LTRNetDecoder( new DecodeConfigLTRNet(), +// mSource.getSampleType(), null, MessageDirection.OSW ); break; case MDC1200: decoder = new MDCDecoder( null ); break; case MPT1327: - decoder = new MPT1327Decoder( new DecodeConfigMPT1327(), - mSource.getSampleType(), null, Sync.NORMAL ); +// decoder = new MPT1327Decoder( new DecodeConfigMPT1327(), +// mSource.getSampleType(), null, Sync.NORMAL ); break; case PASSPORT: - decoder = new PassportDecoder( new DecodeConfigPassport(), - mSource.getSampleType(), null ); +// decoder = new PassportDecoder( new DecodeConfigPassport(), +// mSource.getSampleType(), null ); break; case P25_PHASE1: ResourceManager rm = new ResourceManager(); - decoder = new P25Decoder( rm, mSource.getSampleType(), - (Modulation)mComboModulations.getSelectedItem(), null ); +// decoder = new P25Decoder( rm, mSource.getSampleType(), +// (Modulation)mComboModulations.getSelectedItem(), null ); break; case TAIT_1200: decoder = new Tait1200Decoder( null ); diff --git a/src/instrument/gui/ConstellationPanel.java b/src/instrument/gui/ConstellationPanel.java index ed1069b5..615bda8a 100644 --- a/src/instrument/gui/ConstellationPanel.java +++ b/src/instrument/gui/ConstellationPanel.java @@ -29,11 +29,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.wave.WaveSource.PositionListener; public class ConstellationPanel extends JInternalFrame - implements PositionListener, Listener + implements PositionListener, Listener { private static final long serialVersionUID = 1L; private final static Logger mLog = @@ -69,7 +69,7 @@ public class ConstellationPanel extends JInternalFrame } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mConstellation.receive( sample ); } diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 5836f0bf..494652f6 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -22,6 +22,8 @@ import instrument.tap.Tap; import instrument.tap.TapViewPanel; import instrument.tap.stream.BinaryTap; import instrument.tap.stream.BinaryTapViewPanel; +import instrument.tap.stream.ComplexSampleTap; +import instrument.tap.stream.ComplexSampleTapViewPanel; import instrument.tap.stream.ComplexTap; import instrument.tap.stream.ComplexTapViewPanel; import instrument.tap.stream.DibitTap; @@ -51,18 +53,18 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import message.Message; +import module.decode.Decoder; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import source.Source.SampleType; +import sample.SampleType; import source.wave.ComplexWaveSource; import source.wave.FloatWaveSource; import source.wave.WaveSource; import source.wave.WaveSource.PositionListener; -import decode.Decoder; public class DecoderViewFrame extends JInternalFrame implements PositionListener, Listener @@ -85,12 +87,12 @@ public class DecoderViewFrame extends JInternalFrame if( source.getSampleType() == SampleType.COMPLEX ) { ComplexWaveSource cws = (ComplexWaveSource)mWaveSource; - cws.addListener( mDecoder.getComplexReceiver() ); +// cws.addListener( mDecoder.getComplexReceiver() ); } else if( source.getSampleType() == SampleType.REAL ) { FloatWaveSource fws = (FloatWaveSource)mWaveSource; - fws.setListener( mDecoder.getRealReceiver() ); +// fws.setListener( mDecoder.getRealReceiver() ); } mDecoder.addMessageListener( this ); @@ -102,7 +104,7 @@ public class DecoderViewFrame extends JInternalFrame { setLayout( new MigLayout( "insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]" ) ); - setTitle( "Decoder [" + mDecoder.getType().getDisplayString() + "]" ); + setTitle( "Decoder [" + mDecoder.getDecoderType().getDisplayString() + "]" ); setPreferredSize( new Dimension( 450, 250 ) ); setSize( 450, 250 ); @@ -177,6 +179,9 @@ public class DecoderViewFrame extends JInternalFrame case STREAM_COMPLEX: panel = new ComplexTapViewPanel( (ComplexTap)tap ); break; + case STREAM_COMPLEX_SAMPLE: + panel = new ComplexSampleTapViewPanel( (ComplexSampleTap)tap ); + break; case STREAM_DIBIT: panel = new DibitTapViewPanel( (DibitTap)tap ); break; diff --git a/src/instrument/tap/TapType.java b/src/instrument/tap/TapType.java index dbb3f55d..0c3dd212 100644 --- a/src/instrument/tap/TapType.java +++ b/src/instrument/tap/TapType.java @@ -22,6 +22,7 @@ public enum TapType EVENT_SYNC_DETECT, STREAM_BINARY, STREAM_COMPLEX, + STREAM_COMPLEX_SAMPLE, STREAM_DIBIT, STREAM_EYE_DIAGRAM, STREAM_FLOAT, diff --git a/src/instrument/tap/stream/ComplexSampleTap.java b/src/instrument/tap/stream/ComplexSampleTap.java new file mode 100644 index 00000000..9b7ee035 --- /dev/null +++ b/src/instrument/tap/stream/ComplexSampleTap.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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 instrument.tap.stream; + +import instrument.tap.TapListener; +import instrument.tap.TapType; +import sample.Listener; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; + +public class ComplexSampleTap extends StreamTap implements ComplexSampleListener +{ + private ComplexSampleListener mListener; + + public ComplexSampleTap( String name, + int delay, + float sampleRateRatio ) + { + super( TapType.STREAM_COMPLEX_SAMPLE, name, delay, sampleRateRatio ); + } + + public ComplexSampleTap( TapType tapType, String name, int delay, float sampleRateRatio ) + { + super( tapType, name, delay, sampleRateRatio ); + } + + @Override + public void receive( float inphase, float quadrature ) + { + if( mListener != null ) + { + mListener.receive( inphase, quadrature ); + } + + for( TapListener listener: mListeners ) + { + listener.receive( new Complex( inphase, quadrature ) ); + } + } + + public void setListener( ComplexSampleListener listener ) + { + mListener = listener; + } + + public void removeListener( ComplexSampleListener listener ) + { + mListener = null; + } +} diff --git a/src/instrument/tap/stream/ComplexSampleTapViewPanel.java b/src/instrument/tap/stream/ComplexSampleTapViewPanel.java new file mode 100644 index 00000000..8c5713af --- /dev/null +++ b/src/instrument/tap/stream/ComplexSampleTapViewPanel.java @@ -0,0 +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 + ******************************************************************************/ +package instrument.tap.stream; + +import gui.SDRTrunk; +import instrument.gui.SampleModel; +import instrument.tap.Tap; +import instrument.tap.TapViewPanel; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.util.List; +import java.util.Observable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.complex.Complex; + +public class ComplexSampleTapViewPanel extends TapViewPanel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); + + private Tap mTap; + private List mSamples; + private int mSampleCount; + + public ComplexSampleTapViewPanel( ComplexSampleTap tap ) + { + super( new SampleModel(), tap.getName() ); + + setSize( new Dimension( 400, 400 ) ); + + mTap = tap; + mTap.addListener( getModel() ); + + mSampleCount = (int)( 2000 * tap.getSampleRateRatio() ); + + getModel().setSampleCount( mSampleCount ); + getModel().setDelay( tap.getDelay() ); + + mLog.info( "Complex Tap Panel [" + tap.getName() + "] count: " + + getModel().getSampleCount() + " delay:" + getModel().getDelay() ); + } + + @Override + public void update( Observable arg0, Object arg1 ) + { + mSamples = (List)getModel().getSamples(); + repaint(); + } + + public void paintComponent( Graphics g ) + { + super.paintComponent( g ); + + Graphics2D g2 = (Graphics2D)g; + + int height = getHeight(); + int width = getWidth(); + + g2.setColor( Color.DARK_GRAY ); + + float middle = (float)height / 4.0f; + + g2.drawLine( 0, (int)middle, width, (int)middle ); + g2.drawLine( 0, (int)( 3 * middle ), width, (int)( 3 * middle ) ); + + if( mSamples != null && + mSamples.size() > 0 ) + { + g2.setColor( getForeground() ); + + g2.drawString( mLabel, 5 , 15 ); + + Path2D.Float polylineReal = + new Path2D.Float( Path2D.Float.WIND_EVEN_ODD, mSamples.size() ); + Path2D.Float polylineImag = + new Path2D.Float( Path2D.Float.WIND_EVEN_ODD, mSamples.size() ); + + /* Move to the first point */ + Point2D.Float firstReal = resolve( 0, mSamples.get( 0 ), Type.REAL ); + polylineReal.moveTo( firstReal.x, firstReal.y ); + + Point2D.Float firstImag = resolve( 0, mSamples.get( 0 ), Type.IMAGINERY ); + polylineImag.moveTo( firstImag.x, firstImag.y ); + + for( int x = 1; x < mSamples.size(); x++ ) + { + Point2D.Float pointReal = resolve( x, mSamples.get( x ), Type.REAL ); + polylineReal.lineTo( pointReal.x, pointReal.y ); + + Point2D.Float pointImag = resolve( x, mSamples.get( x ), Type.IMAGINERY ); + polylineImag.lineTo( pointImag.x, pointImag.y ); + +// mLog.debug( "Imag y:" + pointImag.getY() + " height:" + getHeight() ); + } + + g2.draw( polylineReal ); + g2.draw( polylineImag ); + } + + g2.dispose(); + } + + + private Point2D.Float resolve( int index, Complex sample, Type type ) + { + int middle = (int)( getHeight() / 4 ); + + float y = middle - ( middle * mVerticalZoom * ( type == Type.REAL ? + sample.real() : sample.imaginary()) ); + + if( type == Type.IMAGINERY ) + { + y += ( middle * 2 ); + } + + /* Clip y to zero as necessary */ + y = ( y < 0 ) ? 0 : y; + + /* Clip y to max height as necessary */ + y = ( y > getHeight() ) ? getHeight() : y; + + float indexWidth = (float)getWidth() / (float)mSampleCount; + + float x = (float)index * indexWidth; + + Point2D.Float point = new Point2D.Float( x, y ); + + return new Point2D.Float( x , y ); + } + + public enum Type{ REAL,IMAGINERY }; +} diff --git a/src/instrument/tap/stream/ComplexTap.java b/src/instrument/tap/stream/ComplexTap.java index 43b81144..2d6cf9d2 100644 --- a/src/instrument/tap/stream/ComplexTap.java +++ b/src/instrument/tap/stream/ComplexTap.java @@ -20,16 +20,15 @@ package instrument.tap.stream; import instrument.tap.TapListener; import instrument.tap.TapType; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ComplexTap extends StreamTap - implements Listener +public class ComplexTap extends StreamTap implements Listener { - private Listener mListener; + private Listener mListener; public ComplexTap( String name, - int delay, - float sampleRateRatio ) + int delay, + float sampleRateRatio ) { super( TapType.STREAM_COMPLEX, name, delay, sampleRateRatio ); } @@ -40,25 +39,25 @@ public class ComplexTap extends StreamTap } @Override - public void receive( ComplexSample t ) + public void receive( Complex sample ) { if( mListener != null ) { - mListener.receive( t ); + mListener.receive( sample ); } for( TapListener listener: mListeners ) { - listener.receive( t ); + listener.receive( sample ); } } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/instrument/tap/stream/ComplexTapViewPanel.java b/src/instrument/tap/stream/ComplexTapViewPanel.java index 5bc9dda8..a23b997c 100644 --- a/src/instrument/tap/stream/ComplexTapViewPanel.java +++ b/src/instrument/tap/stream/ComplexTapViewPanel.java @@ -34,7 +34,7 @@ import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexTapViewPanel extends TapViewPanel { @@ -42,7 +42,7 @@ public class ComplexTapViewPanel extends TapViewPanel private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); private Tap mTap; - private List mSamples; + private List mSamples; private int mSampleCount; public ComplexTapViewPanel( ComplexTap tap ) @@ -66,7 +66,7 @@ public class ComplexTapViewPanel extends TapViewPanel @Override public void update( Observable arg0, Object arg1 ) { - mSamples = (List)getModel().getSamples(); + mSamples = (List)getModel().getSamples(); repaint(); } @@ -124,7 +124,7 @@ public class ComplexTapViewPanel extends TapViewPanel } - private Point2D.Float resolve( int index, ComplexSample sample, Type type ) + private Point2D.Float resolve( int index, Complex sample, Type type ) { int middle = (int)( getHeight() / 4 ); diff --git a/src/instrument/tap/stream/EyeDiagramData.java b/src/instrument/tap/stream/EyeDiagramData.java index 143269c3..8cd2828b 100644 --- a/src/instrument/tap/stream/EyeDiagramData.java +++ b/src/instrument/tap/stream/EyeDiagramData.java @@ -1,26 +1,32 @@ package instrument.tap.stream; -import sample.complex.ComplexSample; public class EyeDiagramData { - private ComplexSample[] mSamples; + private float[] mSamplesInphase; + private float[] mSamplesQuadrature; private float mLeftPoint; private float mRightPoint; private float mError; - public EyeDiagramData( ComplexSample[] samples, float leftPoint, - float rightPoint, float error ) + public EyeDiagramData( float[] samplesInphase, float[] samplesQuadrature, + float leftPoint, float rightPoint, float error ) { - mSamples = samples; + mSamplesInphase = samplesInphase; + mSamplesQuadrature = samplesQuadrature; mLeftPoint = leftPoint; mRightPoint = rightPoint; mError = error; } - public ComplexSample[] getSamples() + public float[] getInphaseSamples() { - return mSamples; + return mSamplesInphase; + } + + public float[] getQuadratureSamples() + { + return mSamplesQuadrature; } public float getLeftPoint() diff --git a/src/instrument/tap/stream/EyeDiagramDataTap.java b/src/instrument/tap/stream/EyeDiagramDataTap.java index 5358806a..4f446746 100644 --- a/src/instrument/tap/stream/EyeDiagramDataTap.java +++ b/src/instrument/tap/stream/EyeDiagramDataTap.java @@ -17,10 +17,11 @@ ******************************************************************************/ package instrument.tap.stream; +import module.decode.p25.P25_LSMDecoder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.P25Decoder; import instrument.tap.TapListener; import instrument.tap.TapType; import sample.Listener; diff --git a/src/instrument/tap/stream/EyeDiagramDataTapPanel.java b/src/instrument/tap/stream/EyeDiagramDataTapPanel.java index 9edbc6fd..b4cbd84d 100644 --- a/src/instrument/tap/stream/EyeDiagramDataTapPanel.java +++ b/src/instrument/tap/stream/EyeDiagramDataTapPanel.java @@ -32,7 +32,7 @@ import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class EyeDiagramDataTapPanel extends TapViewPanel { @@ -94,10 +94,11 @@ public class EyeDiagramDataTapPanel extends TapViewPanel EyeDiagramData data = mSamples.get( z ); - ComplexSample[] samples = data.getSamples(); + float[] inphaseSamples = data.getInphaseSamples(); + float[] quadratureSamples = data.getQuadratureSamples(); /* Draw the sample set containing two symbol periods of data */ - if( samples.length >= 1 ) + if( inphaseSamples.length >= 1 ) { Path2D.Float iLine = new Path2D.Float( GeneralPath.WIND_EVEN_ODD, 20 ); Path2D.Float qLine = new Path2D.Float( GeneralPath.WIND_EVEN_ODD, 20 ); @@ -105,17 +106,17 @@ public class EyeDiagramDataTapPanel extends TapViewPanel float xAxis = 0; - iLine.moveTo( xAxis, getSampleHeight( halfHeight, samples[ 0 ].inphase() ) ); - qLine.moveTo( xAxis + third, getSampleHeight( halfHeight, samples[ 0 ].quadrature() ) ); - mLine.moveTo( xAxis + ( two_thirds ), getMagnitudeHeight( samples[ 0 ].magnitude() ) ); + iLine.moveTo( xAxis, getSampleHeight( halfHeight, inphaseSamples[ 0 ] ) ); + qLine.moveTo( xAxis + third, getSampleHeight( halfHeight, quadratureSamples[ 0 ] ) ); + mLine.moveTo( xAxis + ( two_thirds ), getMagnitudeHeight( Complex.magnitude( inphaseSamples[ 0 ], quadratureSamples[ 0 ] ) ) ); - for( int x = 1; x < samples.length; x++ ) + for( int x = 1; x < inphaseSamples.length; x++ ) { xAxis += xAxisIncrement; - iLine.lineTo( xAxis, getSampleHeight( halfHeight, samples[ x ].inphase() ) ); - qLine.lineTo( xAxis + third, getSampleHeight( halfHeight, samples[ x ].quadrature() ) ); - mLine.lineTo( xAxis + two_thirds, getMagnitudeHeight( samples[ x ].magnitude() ) ); + iLine.lineTo( xAxis, getSampleHeight( halfHeight, inphaseSamples[ x ] ) ); + qLine.lineTo( xAxis + third, getSampleHeight( halfHeight, quadratureSamples[ x ] ) ); + mLine.lineTo( xAxis + two_thirds, getMagnitudeHeight( Complex.magnitude( inphaseSamples[ x ], quadratureSamples[ x ] ) ) ); } g2.draw( iLine ); diff --git a/src/instrument/tap/stream/FloatBufferTap.java b/src/instrument/tap/stream/FloatBufferTap.java new file mode 100644 index 00000000..55bbfd93 --- /dev/null +++ b/src/instrument/tap/stream/FloatBufferTap.java @@ -0,0 +1,38 @@ +package instrument.tap.stream; + +import sample.Listener; +import sample.real.RealBuffer; + +public class FloatBufferTap extends FloatTap implements Listener +{ + private Listener mListener; + + public FloatBufferTap( String name, int delay, float sampleRateRatio ) + { + super( name, delay, sampleRateRatio ); + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( buffer ); + } + + for( float sample: buffer.getSamples() ) + { + receive( sample ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener( Listener listener ) + { + mListener = null; + } +} diff --git a/src/instrument/tap/stream/QPSKTapPanel.java b/src/instrument/tap/stream/QPSKTapPanel.java index 46d24fd1..f92773f1 100644 --- a/src/instrument/tap/stream/QPSKTapPanel.java +++ b/src/instrument/tap/stream/QPSKTapPanel.java @@ -32,7 +32,7 @@ import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class QPSKTapPanel extends TapViewPanel { @@ -40,7 +40,7 @@ public class QPSKTapPanel extends TapViewPanel private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); private Tap mTap; - private List mSamples; + private List mSamples; private int mSampleCount; public QPSKTapPanel( ComplexTap tap ) @@ -62,7 +62,7 @@ public class QPSKTapPanel extends TapViewPanel @Override public void update( Observable arg0, Object arg1 ) { - mSamples = (List)getModel().getSamples(); + mSamples = (List)getModel().getSamples(); repaint(); } @@ -107,7 +107,7 @@ public class QPSKTapPanel extends TapViewPanel int threshold = (int)( mSamples.size() * 0.9f ); - for( ComplexSample sample: mSamples ) + for( Complex sample: mSamples ) { /* Set color to foreground for newest 10% of constellations */ if( counter == threshold ) diff --git a/src/map/MapService.java b/src/map/MapService.java index 5757e331..0b6c9f87 100644 --- a/src/map/MapService.java +++ b/src/map/MapService.java @@ -29,6 +29,7 @@ import message.Message; import sample.Listener; import settings.SettingsManager; import alias.Alias; +import controller.NamingThreadFactory; import controller.ResourceManager; public class MapService implements Listener @@ -39,7 +40,8 @@ public class MapService implements Listener private static final Color sDEFAULT_COLOR = Color.BLACK; private final ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1); + Executors.newScheduledThreadPool(1, + new NamingThreadFactory( "map tile fetcher" ) ); private ArrayList mListeners = new ArrayList(); diff --git a/src/message/IMessageListener.java b/src/message/IMessageListener.java new file mode 100644 index 00000000..9c176e52 --- /dev/null +++ b/src/message/IMessageListener.java @@ -0,0 +1,8 @@ +package message; + +import sample.Listener; + +public interface IMessageListener +{ + public Listener getMessageListener(); +} diff --git a/src/message/IMessageProvider.java b/src/message/IMessageProvider.java new file mode 100644 index 00000000..4618020a --- /dev/null +++ b/src/message/IMessageProvider.java @@ -0,0 +1,9 @@ +package message; + +import sample.Listener; + +public interface IMessageProvider +{ + public void addMessageListener( Listener listener ); + public void removeMessageListener( Listener listener ); +} diff --git a/src/message/Message.java b/src/message/Message.java index 0defd5ec..ac1ab775 100644 --- a/src/message/Message.java +++ b/src/message/Message.java @@ -23,8 +23,8 @@ import java.util.Date; import java.util.List; import map.Plottable; +import module.decode.event.MessageDetailsProvider; import alias.Alias; -import controller.activity.MessageDetailsProvider; public abstract class Message implements MessageDetailsProvider { diff --git a/src/message/MessageDirection.java b/src/message/MessageDirection.java index 37210c26..87d8e7d2 100644 --- a/src/message/MessageDirection.java +++ b/src/message/MessageDirection.java @@ -19,6 +19,18 @@ package message; public enum MessageDirection { - ISW, //Inbound Status Word - OSW; //Outbound Status Word + ISW( "Mobile (ISW)" ), //Inbound Status Word + OSW( "Tower (OSW)" ); //Outbound Status Word + + private String mLabel; + + private MessageDirection( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/message/MessageFilterFactory.java b/src/message/MessageFilterFactory.java deleted file mode 100644 index 36f8c63a..00000000 --- a/src/message/MessageFilterFactory.java +++ /dev/null @@ -1,111 +0,0 @@ -package message; - -import java.util.ArrayList; -import java.util.List; - -import controller.channel.Channel; -import decode.DecoderType; -import decode.config.AuxDecodeConfiguration; -import decode.fleetsync2.FleetsyncMessageFilter; -import decode.lj1200.LJ1200MessageFilter; -import decode.ltrnet.LTRNetMessageFilter; -import decode.ltrstandard.LTRStandardMessageFilter; -import decode.mdc1200.MDCMessageFilter; -import decode.mpt1327.MPT1327MessageFilter; -import decode.p25.message.filter.P25MessageFilterSet; -import decode.passport.PassportMessageFilter; -import filter.AllPassFilter; -import filter.FilterSet; -import filter.IFilter; - -/** - * Constructs message filters for each of the decoder types - */ -public class MessageFilterFactory -{ - /** - * Assembles a filter set containing filters for the primary channel - * decoder and each of the auxiliary decoders - */ - public static FilterSet getMessageFilter( Channel channel ) - { - FilterSet filterSet = new FilterSet( - "Message Filter: " + channel.getChannelDisplayName() ); - - filterSet.addFilters( getMessageFilters( - channel.getDecodeConfiguration().getDecoderType() ) ); - - AuxDecodeConfiguration auxConfig = channel.getAuxDecodeConfiguration(); - - for( DecoderType decoderType: auxConfig.getAuxDecoders() ) - { - filterSet.addFilters( getMessageFilters( decoderType ) ); - } - - /* If we don't have any filters, add an ALL-PASS filter */ - if( filterSet.getFilters().isEmpty() ) - { - filterSet.addFilter( new AllPassFilter() ); - } - - return filterSet; - } - - /** - * 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> getMessageFilters( DecoderType decoder ) - { - ArrayList> filters = new ArrayList>(); - - switch( decoder ) - { - case ACARS: - break; - case AIS: - break; - case AM: - break; - case APRS: - break; - case DMR: - break; - case FLEETSYNC2: - filters.add( new FleetsyncMessageFilter() ); - break; - case LJ_1200: - filters.add( new LJ1200MessageFilter() ); - break; - case LTR_NET: - filters.add( new LTRNetMessageFilter() ); - break; - case LTR_STANDARD: - filters.add( new LTRStandardMessageFilter() ); - break; - case MDC1200: - filters.add( new MDCMessageFilter() ); - break; - case MOTOROLA_TYPE_2: - break; - case MPT1327: - filters.add( new MPT1327MessageFilter() ); - break; - case NBFM: - break; - case NXDN: - break; - case P25_PHASE1: - case P25_PHASE2: - filters.add( new P25MessageFilterSet() ); - break; - case PASSPORT: - filters.add( new PassportMessageFilter() ); - break; - default: - break; - } - - return filters; - } -} diff --git a/src/module/Module.java b/src/module/Module.java new file mode 100644 index 00000000..27f89420 --- /dev/null +++ b/src/module/Module.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package module; + +public abstract class Module +{ + /** + * Defines the basic component level class for all processing, demodulation + * and decoding components that can operate within a processing chain. + * + * Modules can optionally implement any of the following interfaces: + * + * IAudioPacketProvider produces audio packets + * ICallEventProvider call events + * IChannelEventProvider channel events (reset, selection, start, etc) + * IComplexBufferListener consumes complex samples and normally + * produces demodulated real sample buffers + * IFrequencyCorrectionController provides tuned frequency error corrections + * IMessageProvider produces messages + * IRealBufferListener consumes demodulated real sample buffers + * IRealBufferListener produces demodulated real sample buffers + * ISquelchStateListener consumes squelch states + * ISquelchStateProvider provides squelch states + */ + public Module() + { + } + + /** + * Dispose of all resources and listeners and prepare for garbage collection + */ + public abstract void dispose(); + + /** + * Initializes or reset all internal states to default - prepare to process + * again. This method is usually invoked between stopping a module and + * starting it again processing a newly tuned channel, like a traffic + * channel. + */ + public abstract void init(); +} diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java new file mode 100644 index 00000000..c11e85d0 --- /dev/null +++ b/src/module/ProcessingChain.java @@ -0,0 +1,661 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package module; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import message.IMessageProvider; +import message.Message; +import module.decode.Decoder; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.IDecoderStateEventListener; +import module.decode.state.IDecoderStateEventProvider; +import module.decode.state.State; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; +import sample.real.IRealBufferListener; +import sample.real.IRealBufferProvider; +import sample.real.RealBuffer; +import source.ComplexSource; +import source.RealSource; +import source.Source; +import source.SourceException; +import source.tuner.TunerChannelSource; +import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.IFrequencyCorrectionController; +import alias.Metadata; +import audio.AudioPacket; +import audio.IAudioPacketProvider; +import audio.metadata.IMetadataListener; +import audio.metadata.IMetadataProvider; +import audio.squelch.ISquelchStateListener; +import audio.squelch.ISquelchStateProvider; +import audio.squelch.SquelchState; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; +import controller.channel.ChannelEvent; +import controller.channel.IChannelEventListener; +import controller.channel.IChannelEventProvider; + +/** + * Processing chain provides a framework for connecting a complex or real sample + * source to a set of one primary decoder and zero or more auxiliary decoders. + * All decoded messages and call events produced by the decoders and the decoder + * call states are aggregated by the various broadcasters. You can register + * listeners to receive aggregated messages, call events, and audio packets. + * + * Normal setup sequence: + * + * 1) Add one or more modules + * 2) Register listeners to receive messages, call events, audio, etc. + * 3) Add a valid source + * 4) Invoke the start() method to start processing. + * 5) Invoke the stop() method to stop processing. + * + * Optional: if you want to reuse the processing chain with a new sample source, + * invoke the following method sequence: stop(), reset(), setSource(), start() + */ +public class ProcessingChain implements IChannelEventListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( ProcessingChain.class ); + + private BufferReceiver mComplexReceiver = new BufferReceiver<>( 5 ); + private BufferReceiver mRealReceiver = new BufferReceiver<>( 5 ); + + private Broadcaster mAudioPacketBroadcaster = new Broadcaster<>(); + private Broadcaster mMetadataBroadcaster = new Broadcaster<>(); + private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); + private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); + private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); + private Broadcaster mMessageBroadcaster = new Broadcaster<>(); + private Broadcaster mRealBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); + + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mBufferProcessingTask; + private AtomicBoolean mRunning = new AtomicBoolean(); + + private Source mSource; + private List mModules = new ArrayList<>(); + private IFrequencyCorrectionController mFrequencyCorrectionController; + + public ProcessingChain() + { + this( new ThreadPoolManager() ); + } + + public ProcessingChain( ThreadPoolManager threadManager ) + { + mThreadPoolManager = threadManager; + } + + public void dispose() + { + stop(); + + for( Module module: mModules ) + { + module.dispose(); + } + + mModules.clear(); + + mComplexReceiver.dispose(); + mComplexReceiver = null; + + mRealReceiver.dispose(); + mRealReceiver = null; + + mThreadPoolManager = null; + mBufferProcessingTask = null; + mFrequencyCorrectionController = null; + + mAudioPacketBroadcaster.dispose(); + mCallEventBroadcaster.dispose(); + mChannelEventBroadcaster.dispose(); + mComplexBufferBroadcaster.dispose(); + mMessageBroadcaster.dispose(); + mRealBufferBroadcaster.dispose(); + mSquelchStateBroadcaster.dispose(); + } + + /** + * Indicates if this processing chain is currently receiving samples from + * a source and sending those samples to the decoders. + */ + public boolean processing() + { + return mRunning.get(); + } + + /** + * Indicates if this chain currently has a valid sample source. + */ + public boolean hasSource() + { + return mSource != null; + } + + /** + * Indicates if one of the modules provides frequency error control. + */ + public boolean hasFrequencyCorrectionControl() + { + return mFrequencyCorrectionController != null && + mFrequencyCorrectionController.hasFrequencyCorrectionControl(); + } + + /** + * Frequency correction controller. Can be null. + */ + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + if( mFrequencyCorrectionController != null ) + { + return mFrequencyCorrectionController.getFrequencyCorrectionControl(); + } + + return null; + } + + /** + * Applies a sample source to this processing chain. Processing won't + * start until the start() method is invoked. + * + * @param source - real or complex sample source + * + * @throws IllegalStateException if the processing chain is currently + * processing with another source. Invoke stop() before applying a new + * source. + */ + public void setSource( Source source ) throws IllegalStateException + { + if( processing() ) + { + throw new IllegalStateException( "Processing chain is currently " + + "processing. Invoke stop() on the processing chain before " + + "applying a new sample source" ); + } + + mSource = source; + + /* Establish two-way communication between the tuner channel source and + * the decoder for fine tuning frequency error correction feedback from + * the decoder. The tuner channel source will also notify the frequency + * controller during frequency changes to allow the controller to reset + * frequency error tracking */ + if( mSource != null && + mSource instanceof TunerChannelSource && + hasFrequencyCorrectionControl() ) + { + TunerChannelSource channel = (TunerChannelSource)mSource; + + FrequencyCorrectionControl control = getFrequencyCorrectionControl(); + + channel.setFrequencyChangeListener( control ); + control.setFrequencyChangeListener( channel ); + } + } + + /** + * List of current modules for this processing chain + */ + public List getModules() + { + return mModules; + } + + /** + * List of decoders for this processing chain + */ + public List getDecoders() + { + List decoders = new ArrayList<>(); + + for( Module module: mModules ) + { + if( module instanceof Decoder ) + { + decoders.add( (Decoder)module ); + } + } + + return decoders; + } + + /** + * Adds a module to the processing chain. Each module is tested for the + * interfaces that it supports and is registered or receives a listener + * to consume or produce the supported interface data type. All elements + * that are produced by any component are automatically routed to all other + * components that support the corresponding listener interface. + * + * At least one module should consume complex samples and either produce + * decoded messages and/or audio, or produce decoded real sample buffers + * for all other modules to consume. + * + * @param module - processing module, demodulator or decoder + */ + public void addModule( Module module ) + { + mModules.add( module ); + + /* Listeners */ + if( module instanceof IMetadataListener ) + { + mMetadataBroadcaster.addListener( + ((IMetadataListener)module).getMetadataListener() ); + } + + if( module instanceof IChannelEventListener ) + { + mChannelEventBroadcaster.addListener( + ((IChannelEventListener)module).getChannelEventListener() ); + } + + if( module instanceof IComplexBufferListener ) + { + mComplexBufferBroadcaster.addListener( + ((IComplexBufferListener)module).getComplexBufferListener() ); + } + + if( module instanceof IDecoderStateEventListener ) + { + mDecoderStateEventBroadcaster.addListener( + ((IDecoderStateEventListener)module).getDecoderStateListener() ); + } + + if( module instanceof IRealBufferListener ) + { + mRealBufferBroadcaster.addListener( + ((IRealBufferListener)module).getRealBufferListener() ); + } + + if( module instanceof ISquelchStateListener ) + { + mSquelchStateBroadcaster.addListener( + ((ISquelchStateListener)module).getSquelchStateListener() ); + } + + /* Providers */ + if( module instanceof IMetadataProvider ) + { + ((IMetadataProvider)module).setMetadataListener( + mMetadataBroadcaster ); + } + + if( module instanceof IAudioPacketProvider ) + { + ((IAudioPacketProvider)module).setAudioPacketListener( + mAudioPacketBroadcaster ); + } + + if( module instanceof ICallEventProvider ) + { + ((ICallEventProvider)module).setCallEventListener( mCallEventBroadcaster ); + } + + if( module instanceof IChannelEventProvider ) + { + ((IChannelEventProvider)module).setChannelEventListener( + mChannelEventBroadcaster ); + } + + if( module instanceof IDecoderStateEventProvider ) + { + ((IDecoderStateEventProvider)module).setDecoderStateListener( + mDecoderStateEventBroadcaster ); + } + + if( module instanceof IFrequencyCorrectionController ) + { + mFrequencyCorrectionController = (IFrequencyCorrectionController)module; + } + + if( module instanceof IMessageProvider ) + { + ((IMessageProvider)module).addMessageListener( mMessageBroadcaster ); + } + + if( module instanceof IRealBufferProvider ) + { + ((IRealBufferProvider)module).setRealBufferListener( mRealBufferBroadcaster ); + } + + if( module instanceof ISquelchStateProvider ) + { + ((ISquelchStateProvider)module).setSquelchStateListener( + mSquelchStateBroadcaster ); + } + } + + /** + * Adds the list of modules to this processing chain + */ + public void addModules( List modules ) + { + for( Module module: modules ) + { + addModule( module ); + } + } + + + /** + * Starts processing if the chain has a valid source. Invocations on an + * already started chain have no effect. + */ + public void start() + { + if( mRunning.compareAndSet( false, true ) ) + { + if( mSource != null ) + { + /* Initialize each of the modules */ + for( Module module: mModules ) + { + module.init(); + } + + /* Register with the source to receive sample data. Setup a + * timer task to process the buffer queues 50 times a second + * (every 20 ms) */ + mBufferProcessingTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.DECODER, new BufferProcessor(), 20, TimeUnit.MILLISECONDS ); + + switch( mSource.getSampleType() ) + { + case COMPLEX: + ((ComplexSource)mSource).setListener( mComplexBufferBroadcaster ); + break; + case REAL: + ((RealSource)mSource).setListener( mRealBufferBroadcaster ); + break; + default: + throw new IllegalArgumentException( "Unrecognized source " + + "sample type - cannot start processing chain" ); + } + + /* If this is a tuner source, broadcast the frequency to all + * of the decoder state's */ + if( mSource instanceof TunerChannelSource ) + { + try + { + long frequency = ((TunerChannelSource)mSource).getFrequency(); + + mDecoderStateEventBroadcaster.broadcast( + new DecoderStateEvent( this, Event.SOURCE_FREQUENCY, + State.IDLE, frequency ) ); + } + catch( SourceException e ) + { + mLog.error( "Error getting frequency from tuner channel source", e ); + } + } + } + } + } + + /** + * Stops processing if the chain is currently processing. Invocations on + * an already stopped chain have no effect. + */ + public void stop() + { + if( mRunning.compareAndSet( true, false ) ) + { + if( mSource != null ) + { + switch( mSource.getSampleType() ) + { + case COMPLEX: + ((ComplexSource)mSource).removeListener( mComplexBufferBroadcaster ); + break; + case REAL: + ((RealSource)mSource).removeListener( mRealBufferBroadcaster ); + break; + default: + throw new IllegalArgumentException( "Unrecognized source " + + "sample type - cannot start processing chain" ); + } + + /* Release the source */ + mSource.dispose(); + mSource = null; + } + + if( mBufferProcessingTask != null ) + { + mThreadPoolManager.cancel( mBufferProcessingTask ); + mBufferProcessingTask = null; + } + } + } + + /** + * Adds the listener to receive audio packets from all modules. + */ + public void addAudioPacketListener( Listener listener ) + { + mAudioPacketBroadcaster.addListener( listener ); + } + + public void removeAudioPacketListener( Listener listener ) + { + mAudioPacketBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive call events from all modules. + */ + public void addCallEventListener( Listener listener ) + { + mCallEventBroadcaster.addListener( listener ); + } + + public void removeCallEventListener( Listener listener ) + { + mCallEventBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive call events from all modules. + */ + public void addChannelEventListener( Listener listener ) + { + mChannelEventBroadcaster.addListener( listener ); + } + + public void removeChannelEventListener( Listener listener ) + { + mChannelEventBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive decoder state events from decoder modules + */ + public void addDecoderStateEventListener( Listener listener ) + { + mDecoderStateEventBroadcaster.addListener( listener ); + } + + public void removeDecoderStateEventListener( Listener listener) + { + mDecoderStateEventBroadcaster.removeListener( listener ); + } + + public Listener getDecoderStateEventListener() + { + return mDecoderStateEventBroadcaster; + } + + /** + * Adds the listener to receive decoded messages from all decoders. + */ + public void addMessageListener( Listener listener ) + { + mMessageBroadcaster.addListener( listener ); + } + + /** + * Adds the list of listeners to receive decoded messages from all decoders. + */ + public void addMessageListeners( List> listeners ) + { + for( Listener listener: listeners ) + { + mMessageBroadcaster.addListener( listener ); + } + } + + public void removeMessageListener( Listener listener ) + { + mMessageBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive call events from all modules. + */ + public void addSquelchStateListener( Listener listener ) + { + mSquelchStateBroadcaster.addListener( listener ); + } + + public void removeSquelchStateListener( Listener listener ) + { + mSquelchStateBroadcaster.removeListener( listener ); + } + + public void addRealBufferListener( Listener listener ) + { + mRealBufferBroadcaster.addListener( listener ); + } + + public void removeRealBufferListener( Listener listener ) + { + mRealBufferBroadcaster.removeListener( listener ); + } + + + /** + * Processes complex and real sample buffers and broadcasts buffers to any + * registered listeners. + */ + private class BufferProcessor implements Runnable + { + private AtomicBoolean mProcessing = new AtomicBoolean(); + + @Override + public void run() + { + /* Allow only one instance of this processor to run to ensure + * correct serial processing of incoming buffers. Overlapping + * invocations of this processor by the timer service will end + * immediately without doing anything for cases where a single + * iteration runs longer than the scheduled processing interval */ + if( mProcessing.compareAndSet( false, true ) ) + { + try + { + for( ComplexBuffer buffer: mComplexReceiver.get() ) + { + mComplexBufferBroadcaster.broadcast( buffer ); + } + } + catch( Exception e ) + { + mLog.error( "Error processing complex sample buffers", e ); + } + + try + { + for( RealBuffer buffer: mRealReceiver.get() ) + { + mRealBufferBroadcaster.broadcast( buffer ); + } + } + catch( Exception e ) + { + mLog.error( "Error processing real sample buffers", e ); + } + + mProcessing.set( false ); + } + } + } + + /** + * Buffer receiver - non-blocking receiver to allow calling thread to return + * without blocking on any internal processing. This ensures that we don't + * impair any receiver sample processing. + */ + private class BufferReceiver implements Listener + { + private LinkedTransferQueue mQueue = new LinkedTransferQueue<>(); + private int mMaxFetchSize; + + public BufferReceiver( int maxFetchSize ) + { + mMaxFetchSize = maxFetchSize; + } + + @Override + public void receive( T t ) + { + mQueue.add( t ); + } + + public List get() + { + List buffers = new ArrayList<>(); + + mQueue.drainTo( buffers, mMaxFetchSize ); + + return buffers; + } + + public void dispose() + { + mQueue.clear(); + mQueue = null; + } + } + + @Override + public Listener getChannelEventListener() + { + return mChannelEventBroadcaster; + } +} diff --git a/src/controller/channel/ProcessingChain.java b/src/module/ProcessingChainOld.java similarity index 76% rename from src/controller/channel/ProcessingChain.java rename to src/module/ProcessingChainOld.java index 38a54674..2d97275a 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/module/ProcessingChainOld.java @@ -15,9 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; - -import gui.SDRTrunk; +package module; import java.io.IOException; import java.util.ArrayList; @@ -29,6 +27,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import message.Message; +import module.decode.Decoder; +import module.decode.state.ChannelState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,30 +38,21 @@ import record.wave.ComplexWaveRecorder; import record.wave.FloatWaveRecorder; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealBuffer; import sample.real.RealSampleListener; import source.ComplexSource; import source.RealSource; import source.Source; -import source.SourceException; import source.config.SourceConfigMixer; import source.tuner.Tuner; import source.tuner.TunerChannelSource; -import source.tuner.frequency.AutomaticFrequencyControl; -import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.AutomaticFrequencyControl_R; import alias.AliasList; -import audio.IAudioOutput; -import audio.SquelchListener; -import audio.inverted.IAudioTypeListener; import controller.ResourceManager; import controller.ThreadPoolManager.ThreadType; -import controller.activity.CallEvent; +import controller.channel.Channel; import controller.channel.Channel.ChannelType; -import controller.state.ChannelState; -import controller.state.ChannelState.State; -import decode.Decoder; -import decode.DecoderFactory; import eventlog.EventLogType; import eventlog.EventLogger; @@ -69,9 +60,9 @@ import eventlog.EventLogger; * A channel has all of the pieces needed to wire together a source, decoder, * event logger and recorder and be started and stopped. */ -public class ProcessingChain implements Listener +public class ProcessingChainOld implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); + private final static Logger mLog = LoggerFactory.getLogger( ProcessingChainOld.class ); protected AtomicBoolean mRunning = new AtomicBoolean(); protected Channel mChannel; @@ -79,15 +70,15 @@ public class ProcessingChain implements Listener protected Decoder mDecoder; protected List mRecorders = new ArrayList(); protected ArrayList mEventLoggers = new ArrayList(); - protected AutomaticFrequencyControl mAFC; + protected AutomaticFrequencyControl_R mAFC; protected ChannelState mChannelState; protected ResourceManager mResourceManager; - private ScheduledFuture mProcessorTask; + private ScheduledFuture mSampleProcessorTask; private AliasList mAliasList; private ComplexSampleReceiver mComplexReceiver = new ComplexSampleReceiver(); - private LinkedTransferQueue> mComplexQueue = - new LinkedTransferQueue>(); + private LinkedTransferQueue> mComplexQueue = + new LinkedTransferQueue>(); private RealSampleReceiver mRealReceiver = new RealSampleReceiver(); private LinkedTransferQueue mRealQueue = @@ -95,7 +86,7 @@ public class ProcessingChain implements Listener protected Broadcaster mBroadcaster = new Broadcaster(); - public ProcessingChain( Channel channel, ResourceManager resourceManager ) + public ProcessingChainOld( Channel channel, ResourceManager resourceManager ) { mChannel = channel; mResourceManager = resourceManager; @@ -108,17 +99,17 @@ public class ProcessingChain implements Listener } } - public IAudioOutput getAudioOutput() - { - if( mDecoder != null ) - { - return mDecoder.getAudioOutput(); - } - - return null; - } +// public IAudioOutput getAudioOutput() +// { +// if( mDecoder != null ) +// { +// return mDecoder.getAudioOutput(); +// } +// +// return null; +// } - public Listener> getComplexReceiver() + public Listener> getComplexReceiver() { return mComplexReceiver; } @@ -140,9 +131,9 @@ public class ProcessingChain implements Listener public void dispose() { - if( mProcessorTask != null ) + if( mSampleProcessorTask != null ) { - mResourceManager.getThreadPoolManager().cancel( mProcessorTask ); + mResourceManager.getThreadPoolManager().cancel( mSampleProcessorTask ); } if( mChannelState != null ) @@ -206,7 +197,7 @@ public class ProcessingChain implements Listener /* Flush call events to the logger */ if( getChannel().getChannelType() == ChannelType.STANDARD ) { - getChannelState().getCallEventModel().flush(); +// getChannelState().getCallEventModel().flush(); } updateEventLogging(); @@ -225,14 +216,13 @@ public class ProcessingChain implements Listener return "Channel [" + mChannel.getName() + "] - "; } - public void updateSource() + private void releaseSource() { - /* Cleanup existing source */ if( mSource != null ) { if( mSource instanceof ComplexSource ) { - ((ComplexSource)mSource).removeListener( this.getComplexReceiver() ); +// ((ComplexSource)mSource).removeAttributeListener( this.getComplexReceiver() ); } else if( mSource instanceof RealSource ) { @@ -240,31 +230,38 @@ public class ProcessingChain implements Listener .removeListener( this.getRealReceiver() ); } - if( mProcessorTask != null ) + if( mSampleProcessorTask != null ) { - mResourceManager.getThreadPoolManager().cancel( mProcessorTask ); + mResourceManager.getThreadPoolManager().cancel( mSampleProcessorTask ); } mSource.dispose(); mSource = null; } + } + + private void obtainSource() + { + if( mSource != null ) + { + releaseSource(); + } - /* Get a new source if needed and start the sample processor thread */ if( mChannel.getEnabled() && mResourceManager != null ) { - try - { - mSource = mResourceManager.getSourceManager() - .getSource( ProcessingChain.this ); - } - catch ( SourceException e ) - { - mLog.error( "Couldn't obtain source for processing chain [" + - toString() + "]", e ); - - mSource = null; - } +// try +// { +// mSource = mResourceManager.getSourceManager() +// .getSource( ProcessingChainOld.this ); +// } +// catch ( SourceException e ) +// { +// mLog.error( "Couldn't obtain source for processing chain [" + +// toString() + "]", e ); +// +// mSource = null; +// } if( mSource != null ) { @@ -272,14 +269,14 @@ public class ProcessingChain implements Listener { try { - mProcessorTask = + mSampleProcessorTask = mResourceManager.getThreadPoolManager() .scheduleFixedRate( ThreadType.DECODER, new ComplexProcessor(), 50, TimeUnit.MILLISECONDS ); - ((ComplexSource)mSource) - .setListener( this.getComplexReceiver() ); +// ((ComplexSource)mSource) +// .setListener( this.getComplexReceiver() ); } catch( RejectedExecutionException ree ) { @@ -294,7 +291,7 @@ public class ProcessingChain implements Listener { try { - mProcessorTask = + mSampleProcessorTask = mResourceManager.getThreadPoolManager() .scheduleFixedRate( ThreadType.DECODER, new RealProcessor(), @@ -316,61 +313,67 @@ public class ProcessingChain implements Listener } } + public void updateSource() + { + /* Cleanup existing source */ + releaseSource(); + + /* Get a new source if needed and start the sample processor thread */ + obtainSource(); + } + public void updateDecoder() { if( mChannel.getEnabled() ) { - AliasList aliasList = null; - - if( mResourceManager != null ) + if( mDecoder != null ) { - aliasList = mResourceManager.getPlaylistManager() - .getPlayist().getAliasDirectory().getAliasList( - mChannel.getAliasListName() ); - } - - /* Setup the channel state */ - mChannelState = DecoderFactory.getChannelStateNew( this, aliasList ); - - if( mSource == null ) - { - mChannelState.setState( State.NO_TUNER ); + mDecoder.init(); } else { - mDecoder = DecoderFactory. - getDecoder( this, mSource.getSampleType(), aliasList ); - - if( mDecoder != null ) - { - /* Register to receive decoded messages and auxiliary messages */ - mDecoder.addMessageListener( ProcessingChain.this ); +// /* Setup the channel state */ +// mChannelState = DecoderFactory.getChannelStateNew( this, mAliasList ); - /* Establish two-way communication between the frequency - * correction controller and the tuner channel source */ - if( mSource instanceof TunerChannelSource && - mDecoder.hasFrequencyCorrectionControl() ) - { - TunerChannelSource tcs = (TunerChannelSource)mSource; - - FrequencyCorrectionControl fcc = - mDecoder.getFrequencyCorrectionControl(); - - if( fcc != null ) - { - tcs.addListener( fcc ); - fcc.setListener( tcs ); - } - } - - /* Register audio output to be controlled by the channel state */ - mChannelState.addListener( (SquelchListener)mDecoder.getAudioOutput() ); - mChannelState.setListener( (IAudioTypeListener)mDecoder.getAudioOutput() ); + if( mSource == null ) + { +// mChannelState.setState( State.NO_TUNER ); + } + else + { +// mDecoder = DecoderFactory. +// getDecoder( this, mSource.getSampleType(), mAliasList ); + + if( mDecoder != null ) + { + /* Register to receive decoded messages and auxiliary messages */ + mDecoder.addMessageListener( ProcessingChainOld.this ); + + /* Establish two-way communication between the frequency + * correction controller and the tuner channel source */ +// if( mSource instanceof TunerChannelSource && +// mDecoder.hasFrequencyCorrectionControl() ) +// { +// TunerChannelSource tcs = (TunerChannelSource)mSource; +// +// FrequencyCorrectionControl fcc = +// mDecoder.getFrequencyCorrectionController(); +// +// if( fcc != null ) +// { +// tcs.addListener( fcc ); +// fcc.setListener( tcs ); +// } + } + + /* Register audio output to be controlled by the channel state */ +// mChannelState.addListener( (SquelchListener)mDecoder.getAudioOutput() ); +// mChannelState.setListener( (IAudioTypeListener)mDecoder.getAudioOutput() ); + } } - } } - } +// } /** * Starts, stops or changes event logging @@ -405,8 +408,8 @@ public class ProcessingChain implements Listener logger = mResourceManager.getEventLogManager() .getLogger( this, type ); - getChannelState().getCallEventModel() - .addListener( (Listener)logger ); +// getChannelState().getCallEventModel() +// .addListener( (Listener)logger ); } else { @@ -468,14 +471,14 @@ public class ProcessingChain implements Listener mLog.info( getLogPrefix() + "- started audio recording [" + recorder.getFileName() + "]" ); - mDecoder.addRealSampleListener( (FloatWaveRecorder)recorder ); +// mDecoder.addRealSampleListener( (FloatWaveRecorder)recorder ); } else if( recorder instanceof ComplexWaveRecorder ) { mLog.info( getLogPrefix() + "- started baseband recording [" + recorder.getFileName() + "]" ); - mDecoder.addComplexListener( (ComplexWaveRecorder )recorder ); +// mDecoder.addComplexListener( (ComplexWaveRecorder )recorder ); } } @@ -565,7 +568,7 @@ public class ProcessingChain implements Listener { if( mDecoder != null ) { - mDecoder.addRealSampleListener( listener ); +// mDecoder.addRealSampleListener( listener ); } } @@ -576,7 +579,7 @@ public class ProcessingChain implements Listener { if( mDecoder != null ) { - mDecoder.removeRealListener( listener ); +// mDecoder.removeRealSampleListener( listener ); } } @@ -584,11 +587,11 @@ public class ProcessingChain implements Listener * Adds a listener to receive a copy of complex (I/Q) baseband samples * received by this processing chain */ - public void addComplexListener( Listener listener ) + public void addComplexListener( Listener listener ) { if( mDecoder != null ) { - mDecoder.addComplexListener( listener ); +// mDecoder.addComplexListener( listener ); } else { @@ -601,11 +604,11 @@ public class ProcessingChain implements Listener * Removes the listener from receiving a copy of complex (I/Q) baseband * samples received by this processing chain */ - public void removeComplexListener( Listener listener ) + public void removeComplexListener( Listener listener ) { if( mDecoder != null ) { - mDecoder.removeComplexListener( listener ); +// mDecoder.removeComplexListener( listener ); } else { @@ -663,7 +666,7 @@ public class ProcessingChain implements Listener for( float sample: samples ) { - mDecoder.getRealReceiver().receive( sample ); +// mDecoder.getRealReceiver().receive( sample ); } } @@ -689,18 +692,18 @@ public class ProcessingChain implements Listener { try { - List> sampleSets = - new ArrayList>(); + List> sampleSets = + new ArrayList>(); mComplexQueue.drainTo( sampleSets, 16 ); - for( List samples: sampleSets ) + for( List samples: sampleSets ) { - for( ComplexSample sample: samples ) + for( Complex sample: samples ) { if( sample != null && mDecoder != null ) { - mDecoder.getComplexReceiver().receive( sample ); +// mDecoder.getComplexReceiver().receive( sample ); } } @@ -720,10 +723,10 @@ public class ProcessingChain implements Listener * Internal listener to receive complex (I/Q) baseband samples. Places * received samples into the queue managed by the ComplexProcessor. */ - public class ComplexSampleReceiver implements Listener> + public class ComplexSampleReceiver implements Listener> { @Override - public void receive( List samples ) + public void receive( List samples ) { mComplexQueue.add( samples ); } diff --git a/src/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeComponentEditor.java similarity index 97% rename from src/decode/AuxDecodeComponentEditor.java rename to src/module/decode/AuxDecodeComponentEditor.java index 5013c8f0..209018f5 100644 --- a/src/decode/AuxDecodeComponentEditor.java +++ b/src/module/decode/AuxDecodeComponentEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.util.Collections; import java.util.HashMap; @@ -24,11 +24,11 @@ import java.util.List; import javax.swing.JCheckBox; import javax.swing.SwingUtilities; +import module.decode.config.AuxDecodeConfiguration; import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.ChannelNode; import controller.channel.ChannelValidationException; -import decode.config.AuxDecodeConfiguration; public class AuxDecodeComponentEditor extends AbstractChannelEditor { diff --git a/src/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java similarity index 88% rename from src/decode/DecodeComponentEditor.java rename to src/module/decode/DecodeComponentEditor.java index 8061cea9..6f35e569 100644 --- a/src/decode/DecodeComponentEditor.java +++ b/src/module/decode/DecodeComponentEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.awt.event.ActionEvent; @@ -23,14 +23,16 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; import javax.swing.SwingUtilities; +import net.miginfocom.swing.MigLayout; +import module.decode.config.DecodeConfigFactory; +import module.decode.config.DecodeConfiguration; import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.ChannelNode; import controller.channel.ChannelValidationException; -import decode.config.DecodeConfigFactory; -import decode.config.DecodeConfiguration; public class DecodeComponentEditor extends AbstractChannelEditor { @@ -44,6 +46,8 @@ public class DecodeComponentEditor extends AbstractChannelEditor { super( channelNode ); + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); + /** * ComboBox: Decoders */ @@ -52,7 +56,7 @@ public class DecodeComponentEditor extends AbstractChannelEditor DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( DecoderType type: DecoderType.getAvailableDecoders() ) + for( DecoderType type: DecoderType.getPrimaryDecoders() ) { model.addElement( type ); } @@ -91,15 +95,16 @@ public class DecodeComponentEditor extends AbstractChannelEditor } //Change to the new one - mEditor = DecodeEditorFactory.getPanel( config, mChannelNode ); + mEditor = DecoderFactory.getPanel( config, mChannelNode ); - add( mEditor, "span 2" ); + add( mEditor, "span 2,center" ); revalidate(); repaint(); } } }); + add( new JLabel( "Decoder:" ) ); add( mComboDecoders, "wrap" ); reset(); diff --git a/src/decode/DecodeEditor.java b/src/module/decode/DecodeEditor.java similarity index 91% rename from src/decode/DecodeEditor.java rename to src/module/decode/DecodeEditor.java index 09ef5340..a3e37865 100644 --- a/src/decode/DecodeEditor.java +++ b/src/module/decode/DecodeEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -29,10 +29,11 @@ import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import net.miginfocom.swing.MigLayout; +import module.decode.config.DecodeConfiguration; import controller.Editor; import controller.channel.Channel; import controller.channel.ChannelValidationException; -import decode.config.DecodeConfiguration; public class DecodeEditor extends Editor { @@ -45,6 +46,8 @@ public class DecodeEditor extends Editor public DecodeEditor( DecodeConfiguration config ) { + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow]" ) ); + mConfig = config; if( config.supportsAFC() ) @@ -93,8 +96,8 @@ public class DecodeEditor extends Editor } } ); - add( mAFCMaximumCorrection ); - add( new JLabel( "Correction Limit Hz" ), "growx, push" ); + add( new JLabel( "Correction Limit Hz:" ) ); + add( mAFCMaximumCorrection, "wrap" ); } } diff --git a/src/module/decode/Decoder.java b/src/module/decode/Decoder.java new file mode 100644 index 00000000..ea216ff5 --- /dev/null +++ b/src/module/decode/Decoder.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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 module.decode; + +import message.IMessageProvider; +import message.Message; +import module.Module; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.IDecoderStateEventListener; +import module.decode.state.IDecoderStateEventProvider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; + +public abstract class Decoder extends Module + implements ICallEventProvider, + IDecoderStateEventListener, + IDecoderStateEventProvider, + IMessageProvider +{ + private final static Logger mLog = LoggerFactory.getLogger( Decoder.class ); + + /** + * Broadcasts decoded messages both to the internal decoder state and any + * external registered message listeners + */ + protected Broadcaster mMessageBroadcaster = + new Broadcaster(); + + protected DecoderState mDecoderState; + + /** + * Decoder - parent class for all decoders, demodulators and components. + */ + public Decoder( DecoderState decoderState ) + { + assert( decoderState != null ); + + mDecoderState = decoderState; + mMessageBroadcaster.addListener( mDecoderState ); + } + + @Override + public void dispose() + { + mMessageBroadcaster.dispose(); + mMessageBroadcaster = null; + } + + /** + * Allows decoder to broadcast initialization messages/events so that + * external modules can be correctly configured. This method is invoked + * prior to processing start. + */ + public void init() + { + mDecoderState.init(); + } + + /** + * Identifies the decoder type (ie protocol) + */ + public abstract DecoderType getDecoderType(); + + + /** + * Adds a listener for receiving decoded messages from this decoder + */ + @Override + public void addMessageListener( Listener listener ) + { + mMessageBroadcaster.addListener( listener ); + } + + /** + * Removes the listener from receiving decoded messages from all attached + * decoders + */ + @Override + public void removeMessageListener( Listener listener ) + { + mMessageBroadcaster.removeListener( listener ); + } + + /** + * Decoder State - tracks the state of the decoder + */ + public DecoderState getDecoderState() + { + return mDecoderState; + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderState.getDecoderStateListener(); + } + + @Override + public void setDecoderStateListener( Listener listener ) + { + mDecoderState.setDecoderStateListener( listener ); + } + + @Override + public void removeDecoderStateListener() + { + mDecoderState.removeDecoderStateListener(); + } + + @Override + public void setCallEventListener( Listener listener ) + { + mDecoderState.setCallEventListener( listener ); + } + + @Override + public void removeCallEventListener() + { + mDecoderState.removeCallEventListener(); + } +} diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java new file mode 100644 index 00000000..a02f4a99 --- /dev/null +++ b/src/module/decode/DecoderFactory.java @@ -0,0 +1,416 @@ +/******************************************************************************* + * 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 module.decode; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import message.Message; +import message.MessageDirection; +import module.Module; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfiguration; +import module.decode.fleetsync2.Fleetsync2Decoder; +import module.decode.fleetsync2.FleetsyncMessageFilter; +import module.decode.lj1200.LJ1200Decoder; +import module.decode.lj1200.LJ1200MessageFilter; +import module.decode.ltrnet.DecodeConfigLTRNet; +import module.decode.ltrnet.LTRNetDecoder; +import module.decode.ltrnet.LTRNetDecoderPanel; +import module.decode.ltrnet.LTRNetEditor; +import module.decode.ltrnet.LTRNetMessageFilter; +import module.decode.ltrstandard.DecodeConfigLTRStandard; +import module.decode.ltrstandard.LTRStandardDecoder; +import module.decode.ltrstandard.LTRStandardDecoderPanel; +import module.decode.ltrstandard.LTRStandardEditor; +import module.decode.ltrstandard.LTRStandardMessageFilter; +import module.decode.mdc1200.MDCDecoder; +import module.decode.mdc1200.MDCMessageFilter; +import module.decode.mpt1327.DecodeConfigMPT1327; +import module.decode.mpt1327.MPT1327ConfigEditor; +import module.decode.mpt1327.MPT1327Decoder; +import module.decode.mpt1327.MPT1327Decoder.Sync; +import module.decode.mpt1327.MPT1327DecoderPanel; +import module.decode.mpt1327.MPT1327MessageFilter; +import module.decode.nbfm.NBFMDecoder; +import module.decode.nbfm.NBFMDecoderPanel; +import module.decode.nbfm.NBFMEditor; +import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.p25.P25Decoder; +import module.decode.p25.P25Decoder.Modulation; +import module.decode.p25.P25DecoderPanel; +import module.decode.p25.P25Editor; +import module.decode.p25.P25_C4FMDecoder; +import module.decode.p25.P25_LSMDecoder; +import module.decode.p25.audio.P25AudioModule; +import module.decode.p25.message.filter.P25MessageFilterSet; +import module.decode.passport.PassportEditor; +import module.decode.passport.PassportMessageFilter; +import module.decode.state.DecoderPanel; +import module.decode.state.TrafficChannelManager; +import module.decode.tait.Tait1200Decoder; +import module.demodulate.fm.FMDemodulatorModule; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import properties.SystemProperties; +import settings.SettingsManager; +import util.TimeStamp; +import alias.AliasList; +import audio.AudioModule; +import controller.ResourceManager; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelNode; +import controller.channel.map.ChannelMap; +import filter.AllPassFilter; +import filter.FilterSet; +import filter.IFilter; + +public class DecoderFactory +{ + private final static Logger mLog = LoggerFactory.getLogger( DecoderFactory.class ); + + public static final boolean NO_DC_REMOVAL = false; + public static final boolean REMOVE_DC = true; + + public static final int NO_FREQUENCY_CORRECTION = 0; + + /** + * Returns a list of one primary decoder and any auxiliary decoders, as + * specified in the configurations. + * + * @param config - primary decoder configuration + * @param auxConfig - auxiliary decoder(s) configuration + * + * @return list of configured decoders + */ + public static List getModules( ChannelType channelType, + ResourceManager resourceManager, + DecodeConfiguration config, + AuxDecodeConfiguration auxConfig, + AliasList aliasList ) + { + List modules = getPrimaryModules( channelType, config, + aliasList, resourceManager ); + + modules.addAll( getAuxiliaryDecoders( auxConfig, aliasList ) ); + + return modules; + } + + public static FMDemodulatorModule getFMDemodulator( DecodeConfiguration config, int pass, int stop, boolean removeDC ) + { + if( config.isAFCEnabled() ) + { + return new FMDemodulatorModule( pass, stop, config.getAFCMaximumCorrection(), removeDC ); + } + else + { + return new FMDemodulatorModule( pass, stop, NO_FREQUENCY_CORRECTION, removeDC ); + } + } + + /** + * Constructs a primary decoder as specified in the decode configuration + * @param config - primary decoder configuration + * @param aliasList - optional alias list + * @return configured decoder or null + */ + public static List getPrimaryModules( ChannelType channelType, + DecodeConfiguration config, + AliasList aliasList, + ResourceManager resourceManager ) + { + List modules = new ArrayList(); + + /* Baseband low-pass filter pass and stop frequencies */ + int pass = config.getDecoderType().getChannelBandwidth() / 2; + int stop = pass + 1250; + + switch( config.getDecoderType() ) + { + case AM: +// decoder = new AMDecoder(); + break; + case NBFM: + modules.add( new AudioModule() ); + modules.add( new NBFMDecoder( config ) ); + modules.add( getFMDemodulator( config, pass, stop, REMOVE_DC ) ); + break; + case LTR_STANDARD: + modules.add( new AudioModule( REMOVE_DC ) ); + MessageDirection direction = ((DecodeConfigLTRStandard)config).getMessageDirection(); + modules.add( new LTRStandardDecoder( aliasList, direction ) ); + modules.add( getFMDemodulator( config, pass, stop, NO_DC_REMOVAL ) ); + break; + case LTR_NET: + modules.add( new AudioModule( REMOVE_DC ) ); + modules.add( new LTRNetDecoder( (DecodeConfigLTRNet)config, aliasList ) ); +// modules.add( new ComplexBufferRecorderModule( getRecordingFilename( "decimated_baseband" ) ) ); + modules.add( getFMDemodulator( config, pass, stop, NO_DC_REMOVAL ) ); + break; + case MPT1327: + DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)config; + + modules.add( new AudioModule() ); + + ChannelMap channelMap = resourceManager.getPlaylistManager().getPlayist() + .getChannelMapList().getChannelMap( mptConfig.getChannelMapName() ); + Sync sync = mptConfig.getSync(); + modules.add( new MPT1327Decoder( channelMap, channelType, aliasList, sync ) ); + + if( channelType == ChannelType.STANDARD ) + { + long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds + + modules.add( new TrafficChannelManager( resourceManager, + config, timeout, + mptConfig.getTrafficChannelPoolSize() ) ); + } + +// modules.add( new RealBufferRecorderModule( getRecordingFilename( "demodulated_mpt1327" ) ) ); + modules.add( getFMDemodulator( config, pass, stop, REMOVE_DC ) ); + break; + case PASSPORT: +// decoder = new PassportDecoder( config, aliasList ); + break; + case P25_PHASE1: + DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)config; + + Modulation modulation = p25Config.getModulation(); + + switch( modulation ) + { + case C4FM: + modules.add( getFMDemodulator( config, 6750, 7500, REMOVE_DC ) ); + modules.add( new P25_C4FMDecoder( aliasList, channelType ) ); + break; + case CQPSK: + modules.add( new P25_LSMDecoder( aliasList, channelType ) ); + break; + default: + throw new IllegalArgumentException( "Unrecognized P25 " + + "Phase 1 Modulation [" + modulation + "]" ); + } + + if( channelType == ChannelType.STANDARD ) + { + long timeout = p25Config.getCallTimeout() * 1000; //convert to milliseconds + + modules.add( new TrafficChannelManager( resourceManager, + config, timeout, + p25Config.getTrafficChannelPoolSize() ) ); + } + + modules.add( new P25AudioModule() ); + break; + default: + throw new IllegalArgumentException( + "Unknown decoder type [" + + config.getDecoderType().toString() + "]" ); + } + + return modules; + } + + /** + * 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 ) + { + List decoders = new ArrayList(); + + for( DecoderType auxDecoder: config.getAuxDecoders() ) + { + switch( auxDecoder ) + { + case FLEETSYNC2: + decoders.add( new Fleetsync2Decoder( aliasList ) ); + break; + case MDC1200: + decoders.add( new MDCDecoder( aliasList ) ); + break; + case LJ_1200: + decoders.add( new LJ1200Decoder( aliasList ) ); + break; + case TAIT_1200: + decoders.add( new Tait1200Decoder( aliasList ) ); + break; + } + } + + return decoders; + } + + public static DecoderPanel getDecoderPanel( SettingsManager settingsManager, + Decoder decoder ) + { + switch( decoder.getDecoderType() ) + { +// case AM: +// break; +// case FLEETSYNC2: +// break; +// case LJ_1200: +// break; + case LTR_NET: + return new LTRNetDecoderPanel( settingsManager, (LTRNetDecoder)decoder ); + case LTR_STANDARD: + return new LTRStandardDecoderPanel( settingsManager, (LTRStandardDecoder)decoder ); +// case MDC1200: +// break; + case MPT1327: + return new MPT1327DecoderPanel( settingsManager, (MPT1327Decoder)decoder ); + case NBFM: + return new NBFMDecoderPanel( settingsManager, decoder ); + case P25_PHASE1: + return new P25DecoderPanel( settingsManager, (P25Decoder)decoder ); +// case PASSPORT: +// break; + case TAIT_1200: + break; + default: + throw new IllegalArgumentException( "Unrecognized decoder: " + + decoder.getDecoderType().getDisplayString() ); + } + + return null; + } + + /** + * Assembles a filter set containing filters for the primary channel + * decoder and each of the auxiliary decoders + */ + public static FilterSet getMessageFilters( List modules ) + { + FilterSet filterSet = new FilterSet<>(); + + for( Module module: modules ) + { + if( module instanceof Decoder ) + { + filterSet.addFilters( getMessageFilter( + ((Decoder)module).getDecoderType() ) ); + } + } + + /* If we don't have any filters, add an ALL-PASS filter */ + if( filterSet.getFilters().isEmpty() ) + { + filterSet.addFilter( new AllPassFilter() ); + } + + return filterSet; + } + + /** + * 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 ) + { + ArrayList> filters = new ArrayList>(); + + switch( decoder ) + { + case FLEETSYNC2: + filters.add( new FleetsyncMessageFilter() ); + break; + case LJ_1200: + filters.add( new LJ1200MessageFilter() ); + break; + case LTR_NET: + filters.add( new LTRNetMessageFilter() ); + break; + case LTR_STANDARD: + filters.add( new LTRStandardMessageFilter() ); + break; + case MDC1200: + filters.add( new MDCMessageFilter() ); + break; + case MPT1327: + filters.add( new MPT1327MessageFilter() ); + break; + case P25_PHASE1: + case P25_PHASE2: + filters.add( new P25MessageFilterSet() ); + break; + case PASSPORT: + filters.add( new PassportMessageFilter() ); + break; + default: + break; + } + + return filters; + } + + public static DecodeEditor getPanel( DecodeConfiguration config, + ChannelNode channelNode ) + { + DecodeEditor configuredPanel; + + switch( config.getDecoderType() ) + { + case NBFM: + configuredPanel = new NBFMEditor( config ); + break; + case LTR_STANDARD: + configuredPanel = new LTRStandardEditor( config ); + break; + case LTR_NET: + configuredPanel = new LTRNetEditor( config ); + break; + case MPT1327: + configuredPanel = new MPT1327ConfigEditor( config, channelNode ); + break; + case PASSPORT: + configuredPanel = new PassportEditor( config ); + break; + case P25_PHASE1: + configuredPanel = new P25Editor( config ); + break; + default: + configuredPanel = new DecodeEditor( config ); + break; + } + + return configuredPanel; + } + + public static String getRecordingFilename( String token ) + { + //Get the base recording filename + StringBuilder sb = new StringBuilder(); + sb.append( SystemProperties.getInstance() + .getApplicationFolder( "recordings" ) ); + sb.append( File.separator ); + sb.append( TimeStamp.getTimeStamp( "_" ) ); + sb.append( "_" ); + sb.append( token ); + + return sb.toString(); + } +} diff --git a/src/decode/DecoderType.java b/src/module/decode/DecoderType.java similarity index 96% rename from src/decode/DecoderType.java rename to src/module/decode/DecoderType.java index 98217436..ca7fee49 100644 --- a/src/decode/DecoderType.java +++ b/src/module/decode/DecoderType.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.util.ArrayList; import java.util.EnumSet; @@ -33,7 +33,7 @@ public enum DecoderType FLEETSYNC2( "Fleetsync II", "Fsync2", "images/fm.png", 12500 ), LJ_1200( "LJ1200 173.075", "LJ1200", "images/lj1200.png", 12500 ), LTR_STANDARD( "LTR-Standard", "LTR", "images/ltr.png", 10000 ), - LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net.png", 10000 ), + LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net.png", 12500 ), MDC1200( "MDC1200", "MDC1200", "images/fm.png", 12500 ), MOTOROLA_TYPE_2( "MOTO T2", "MOTO T2", "images/motorola_type_2.png", 15000 ), MPT1327( "MPT1327", "MPT1327", "images/mpt1327.png", 12500 ), @@ -61,7 +61,7 @@ public enum DecoderType /** * Primary decoders */ - public static EnumSet getAvailableDecoders() + public static EnumSet getPrimaryDecoders() { return EnumSet.of( DecoderType.AM, DecoderType.LTR_NET, diff --git a/src/module/decode/PrimaryDecoder.java b/src/module/decode/PrimaryDecoder.java new file mode 100644 index 00000000..fa7f2c10 --- /dev/null +++ b/src/module/decode/PrimaryDecoder.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package module.decode; + +import module.decode.config.DecodeConfiguration; +import module.decode.state.DecoderState; +import sample.Listener; +import audio.squelch.ISquelchStateProvider; +import audio.squelch.SquelchState; + +/** + * Primary decoder adds the following functionality over the basic decoder: + * + * - Provides audio squelch control + */ + +public abstract class PrimaryDecoder extends Decoder implements ISquelchStateProvider +{ + protected Listener mSquelchStateListener; + + protected DecodeConfiguration mDecodeConfiguration; + + public PrimaryDecoder( DecodeConfiguration config, DecoderState decoderState ) + { + super( decoderState ); + + mDecodeConfiguration = config; + } + + public DecodeConfiguration getDecodeConfiguration() + { + return mDecodeConfiguration; + } + + @Override + public void dispose() + { + super.dispose(); + + mSquelchStateListener = null; + mDecodeConfiguration = null; + } + + public void broadcast( SquelchState state ) + { + if( mSquelchStateListener != null ) + { + mSquelchStateListener.receive( state ); + } + } + + @Override + public void setSquelchStateListener( Listener listener ) + { + mSquelchStateListener = listener; + } + + @Override + public void removeSquelchStateListener() + { + mSquelchStateListener = null; + } +} diff --git a/src/decode/config/DecodeConfigMPT1327.java b/src/module/decode/am/AMDecoder.java similarity index 55% rename from src/decode/config/DecodeConfigMPT1327.java rename to src/module/decode/am/AMDecoder.java index fd68c706..e736215e 100644 --- a/src/decode/config/DecodeConfigMPT1327.java +++ b/src/module/decode/am/AMDecoder.java @@ -1,57 +1,38 @@ -/******************************************************************************* - * 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 decode.config; - -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; - -import decode.DecoderType; -import decode.mpt1327.MPT1327Decoder.Sync; - -public class DecodeConfigMPT1327 extends DecodeConfiguration -{ - private String mChannelMapName; - private Sync mSync = Sync.NORMAL; - - public DecodeConfigMPT1327() - { - super( DecoderType.MPT1327 ); - } - - @XmlElement - public String getChannelMapName() - { - return mChannelMapName; - } - - public void setChannelMapName( String name ) - { - mChannelMapName = name; - } - - @XmlAttribute( name="sync" ) - public Sync getSync() - { - return mSync; - } - - public void setSync( Sync sync ) - { - mSync = sync; - } -} +/******************************************************************************* + * 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 module.decode.am; + +import module.decode.DecoderType; +import module.decode.PrimaryDecoder; +import module.decode.config.DecodeConfiguration; +import module.decode.state.AlwaysUnsquelchedDecoderState; + +public class AMDecoder extends PrimaryDecoder +{ + public AMDecoder( DecodeConfiguration config ) + { + super( config, new AlwaysUnsquelchedDecoderState( "AM" ) ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.NBFM; + } +} diff --git a/src/decode/tait/Tait1200Panel.java b/src/module/decode/am/AMDecoderPanel.java similarity index 57% rename from src/decode/tait/Tait1200Panel.java rename to src/module/decode/am/AMDecoderPanel.java index 10db4912..1bb4f220 100644 --- a/src/decode/tait/Tait1200Panel.java +++ b/src/module/decode/am/AMDecoderPanel.java @@ -15,56 +15,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.am; import javax.swing.JLabel; -import net.miginfocom.swing.MigLayout; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; -public class Tait1200Panel extends AuxStatePanel +public class AMDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private static final String PROTOCOL = "TAIT-1200"; - private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mProtocol = new JLabel( "AM" ); - public Tait1200Panel( SettingsManager settingsManager, Tait1200ChannelState state ) + public AMDecoderPanel( SettingsManager settingsManager, AMDecoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } - public void dispose() + public void init() { - super.dispose(); - } - - public Tait1200ChannelState getState() - { - return (Tait1200ChannelState)mAuxChannelState; - } - - private void init() - { - setOpaque( false ); //Use the parent panel's background color - - setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); + mProtocol.setFont( mFontDecoder ); - addLabel( mProtocol, false ); + add( mProtocol, "wrap" ); } @Override public void receive( final ChangedAttribute changedAttribute ) { } - + @Override public void settingChanged( Setting setting ) { @@ -72,12 +57,18 @@ public class Tait1200Panel extends AuxStatePanel if( setting instanceof ColorSetting ) { - ColorSetting color = (ColorSetting)setting; - - if( color.getColorSettingName() == - ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) { - mProtocol.setForeground( color.getColor() ); + case CHANNEL_STATE_LABEL_DECODER: + if( mProtocol != null ) + { + mProtocol.setForeground( mColorLabelDecoder ); + } + break; + default: + break; } } } diff --git a/src/decode/am/AMEditor.java b/src/module/decode/am/AMEditor.java similarity index 90% rename from src/decode/am/AMEditor.java rename to src/module/decode/am/AMEditor.java index 6380a97f..69561043 100644 --- a/src/decode/am/AMEditor.java +++ b/src/module/decode/am/AMEditor.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.am; +package module.decode.am; -import decode.DecodeEditor; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class AMEditor extends DecodeEditor { diff --git a/src/decode/config/DecodeConfigAM.java b/src/module/decode/am/DecodeConfigAM.java similarity index 91% rename from src/decode/config/DecodeConfigAM.java rename to src/module/decode/am/DecodeConfigAM.java index 1ba26b68..844e2543 100644 --- a/src/decode/config/DecodeConfigAM.java +++ b/src/module/decode/am/DecodeConfigAM.java @@ -15,9 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.am; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigAM extends DecodeConfiguration { diff --git a/src/decode/config/AuxDecodeConfiguration.java b/src/module/decode/config/AuxDecodeConfiguration.java similarity index 96% rename from src/decode/config/AuxDecodeConfiguration.java rename to src/module/decode/config/AuxDecodeConfiguration.java index dcb31f40..044269b8 100644 --- a/src/decode/config/AuxDecodeConfiguration.java +++ b/src/module/decode/config/AuxDecodeConfiguration.java @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.config; import java.util.ArrayList; import javax.xml.bind.annotation.XmlElement; +import module.decode.DecoderType; import controller.config.Configuration; -import decode.DecoderType; public class AuxDecodeConfiguration extends Configuration { diff --git a/src/decode/config/DecodeConfigFactory.java b/src/module/decode/config/DecodeConfigFactory.java similarity index 82% rename from src/decode/config/DecodeConfigFactory.java rename to src/module/decode/config/DecodeConfigFactory.java index 449309d9..a4a88398 100644 --- a/src/decode/config/DecodeConfigFactory.java +++ b/src/module/decode/config/DecodeConfigFactory.java @@ -15,9 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.config; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.am.DecodeConfigAM; +import module.decode.ltrnet.DecodeConfigLTRNet; +import module.decode.ltrstandard.DecodeConfigLTRStandard; +import module.decode.mpt1327.DecodeConfigMPT1327; +import module.decode.nbfm.DecodeConfigNBFM; +import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.passport.DecodeConfigPassport; public class DecodeConfigFactory { diff --git a/src/decode/config/DecodeConfiguration.java b/src/module/decode/config/DecodeConfiguration.java similarity index 74% rename from src/decode/config/DecodeConfiguration.java rename to src/module/decode/config/DecodeConfiguration.java index b5ec7bb8..b6b59f04 100644 --- a/src/decode/config/DecodeConfiguration.java +++ b/src/module/decode/config/DecodeConfiguration.java @@ -15,14 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.config; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import module.decode.DecoderType; +import module.decode.am.DecodeConfigAM; +import module.decode.ltrnet.DecodeConfigLTRNet; +import module.decode.ltrstandard.DecodeConfigLTRStandard; +import module.decode.mpt1327.DecodeConfigMPT1327; +import module.decode.nbfm.DecodeConfigNBFM; +import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.passport.DecodeConfigPassport; import controller.config.Configuration; -import decode.DecoderType; @XmlSeeAlso( { DecodeConfigAM.class, DecodeConfigNBFM.class, @@ -34,6 +41,14 @@ import decode.DecoderType; @XmlRootElement( name = "decode_configuration" ) public abstract class DecodeConfiguration extends Configuration { + public static final int DEFAULT_CALL_TIMEOUT_SECONDS = 45; + public static final int CALL_TIMEOUT_MINIMUM = 10; + public static final int CALL_TIMEOUT_MAXIMUM = 300; //5 minutes + + public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3; + public static final int TRAFFIC_CHANNEL_LIMIT_MINIMUM = 0; + public static final int TRAFFIC_CHANNEL_LIMIT_MAXIMUM = 30; + private DecoderType mDecoderType = DecoderType.NBFM; private boolean mAFCEnabled = true; private int mAFCMaxCorrection = 3000; diff --git a/src/controller/activity/ActivitySummaryFrame.java b/src/module/decode/event/ActivitySummaryFrame.java similarity index 98% rename from src/controller/activity/ActivitySummaryFrame.java rename to src/module/decode/event/ActivitySummaryFrame.java index 8541ccb5..18188d2a 100644 --- a/src/controller/activity/ActivitySummaryFrame.java +++ b/src/module/decode/event/ActivitySummaryFrame.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import java.awt.Component; import java.awt.EventQueue; diff --git a/src/controller/activity/ActivitySummaryProvider.java b/src/module/decode/event/ActivitySummaryProvider.java similarity index 93% rename from src/controller/activity/ActivitySummaryProvider.java rename to src/module/decode/event/ActivitySummaryProvider.java index 91e84253..e113c99c 100644 --- a/src/controller/activity/ActivitySummaryProvider.java +++ b/src/module/decode/event/ActivitySummaryProvider.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import message.Message; import sample.Listener; public interface ActivitySummaryProvider extends Listener { - public String getSummary(); + public String getActivitySummary(); } diff --git a/src/controller/activity/CallEvent.java b/src/module/decode/event/CallEvent.java similarity index 84% rename from src/controller/activity/CallEvent.java rename to src/module/decode/event/CallEvent.java index 84e854f2..1cfcf9ca 100644 --- a/src/controller/activity/CallEvent.java +++ b/src/module/decode/event/CallEvent.java @@ -15,16 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import java.text.SimpleDateFormat; import java.util.Date; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; -import decode.DecoderType; +import alias.MetadataType; -public abstract class CallEvent +public abstract class CallEvent implements Comparable { protected long mEventStart = System.currentTimeMillis(); protected long mEventEnd; @@ -38,6 +39,8 @@ public abstract class CallEvent protected String mToID; protected String mDetails; + private boolean mValid = true; + public CallEvent( DecoderType decoder, CallEventType callEventType, AliasList aliasList, @@ -53,11 +56,31 @@ public abstract class CallEvent mDetails = details; } + /** + * Indicates if this event is valid. Use this method to mark a previously + * dispatched event as invalid and resend it so that any downstream + * processing can remove the invalid event. + */ + public void setValid( boolean valid ) + { + mValid = valid; + } + + public boolean isValid() + { + return mValid; + } + public void setEnd( long end ) { mEventEnd = end; } + public void end() + { + setEnd( System.currentTimeMillis() ); + } + public DecoderType getDecoderType() { return mDecoderType; @@ -67,6 +90,11 @@ public abstract class CallEvent { return mCallEventType; } + + public void setCallEventType( CallEventType type ) + { + mCallEventType = type; + } public AliasList getAliasList() { @@ -103,6 +131,11 @@ public abstract class CallEvent return mFromID != null; } + public MetadataType getFromIDType() + { + return MetadataType.FROM_TALKGROUP; + } + public void setFromID( String id ) { mFromID = id; @@ -123,6 +156,11 @@ public abstract class CallEvent mToID = id; } + public MetadataType getToIDType() + { + return MetadataType.TO; + } + public String getDetails() { return mDetails; @@ -219,6 +257,7 @@ public abstract class CallEvent public enum CallEventType { + ANNOUNCEMENT( "Announcement" ), CALL( "Call" ), CALL_ALERT( "Call Alert" ), CALL_END( "Call End" ), @@ -241,6 +280,7 @@ public abstract class CallEvent REGISTER_ESN( "ESN" ), RESPONSE( "Response" ), SDM( "Short Data Message" ), + STATION_ID( "Station ID" ), STATUS( "Status" ), TELEPHONE_CALL( "Telephone Call" ), UNKNOWN( "Unknown" ), @@ -263,4 +303,10 @@ public abstract class CallEvent return mLabel; } } + + @Override + public int compareTo( CallEvent other ) + { + return Long.compare( mEventStart, other.getEventStartTime() ); + } } diff --git a/src/controller/activity/CallEventAliasCellRenderer.java b/src/module/decode/event/CallEventAliasCellRenderer.java similarity index 98% rename from src/controller/activity/CallEventAliasCellRenderer.java rename to src/module/decode/event/CallEventAliasCellRenderer.java index 88c35bd4..6ef081ee 100644 --- a/src/controller/activity/CallEventAliasCellRenderer.java +++ b/src/module/decode/event/CallEventAliasCellRenderer.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import javax.swing.ImageIcon; import javax.swing.table.DefaultTableCellRenderer; diff --git a/src/controller/activity/CallEventModel.java b/src/module/decode/event/CallEventModel.java similarity index 52% rename from src/controller/activity/CallEventModel.java rename to src/module/decode/event/CallEventModel.java index 5ba9da80..38b5d40d 100644 --- a/src/controller/activity/CallEventModel.java +++ b/src/module/decode/event/CallEventModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,33 +15,31 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; -import java.awt.EventQueue; import java.text.DecimalFormat; import java.text.SimpleDateFormat; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.List; import javax.swing.table.AbstractTableModel; +import module.decode.event.CallEvent.CallEventType; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; -import controller.activity.CallEvent.CallEventType; -public class CallEventModel extends AbstractTableModel +public class CallEventModel extends AbstractTableModel implements Listener { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( CallEventModel.class ); - + + private final static Logger mLog = LoggerFactory.getLogger( CallEventModel.class ); + private static DecimalFormat mFrequencyFormatter = new DecimalFormat( "0.000000" ); - private static final int DEFAULT_ICON_HEIGHT = 14; - public static final int TIME = 0; public static final int EVENT = 1; public static final int FROM_ID = 2; @@ -54,7 +52,7 @@ public class CallEventModel extends AbstractTableModel protected int mMaxMessages = 500; - protected LinkedList mEvents = new LinkedList(); + protected List mEvents = new ArrayList(); protected String[] mHeaders = new String[] { "Time", "Event", @@ -68,7 +66,6 @@ public class CallEventModel extends AbstractTableModel private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" ); - private Broadcaster mBroadcaster = new Broadcaster(); private boolean mNewMessagesFirst = true; public CallEventModel() @@ -77,24 +74,14 @@ public class CallEventModel extends AbstractTableModel public void dispose() { - synchronized( mEvents ) - { - mEvents.clear(); - } - - mBroadcaster.dispose(); + mEvents.clear(); } - public void flush() + public void reset() { - /* Flush all events to listeners ( normally the call event logger ) */ - synchronized( mEvents ) - { - for( CallEvent event: mEvents ) - { - mBroadcaster.receive( event ); - } - } + mEvents.clear(); + + fireTableDataChanged(); } public int getMaxMessageCount() @@ -106,139 +93,57 @@ public class CallEventModel extends AbstractTableModel { mMaxMessages = count; } - - public void add( final CallEvent event ) - { - try - { - synchronized ( mEvents ) - { - mEvents.addFirst( event ); - } - fireTableRowsInserted( 0, 0 ); - - prune(); - } - catch( Exception e ) - { - e.printStackTrace(); - } - } - - public void remove( final CallEvent event ) + /** + * 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 ) { - EventQueue.invokeLater( new Runnable() + //TODO: reverse this so that the new events go to the end and the oldest + //events are at the beginning ... use the getObjectAt() to control + //correct access to the ordering imposed. + + if( event.isValid() ) { - @Override - public void run() - { - synchronized ( mEvents ) - { - final int row = mEvents.indexOf( event ); - - mEvents.remove( event ); - - if( row != -1 ) - { - fireTableRowsDeleted( row, row ); - } - } - } - } ); - } - - public int indexOf( CallEvent event ) - { - return mEvents.indexOf( event ); - } - - public void setEnd( final CallEvent event ) - { - try - { - EventQueue.invokeAndWait( new Runnable() + if( !mEvents.contains( event ) ) { - @Override - public void run() - { - if( event != null ) - { - event.setEnd( System.currentTimeMillis() ); + mEvents.add( 0, event ); + + fireTableRowsInserted( 0, 0 ); - synchronized ( mEvents ) - { - int row = indexOf( event ); - - if( row != -1 ) - { - fireTableCellUpdated( row, TIME ); - } - } - } - else - { - mLog.error( "CallEventModel - couldn't log end time - event was null" ); - } - } - } ); + prune(); + } + else + { + int row = mEvents.indexOf( event ); + + fireTableRowsUpdated( row, row ); + } } - catch( Exception e ) + else { - e.printStackTrace(); + if( mEvents.contains( event ) ) + { + int row = mEvents.indexOf( event ); + + mEvents.remove( event ); + + fireTableRowsDeleted( row, row ); + } } } - public void setFromID( final CallEvent event, final String from ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - if( event != null ) - { - event.setFromID( from ); - - synchronized ( mEvents ) - { - int row = indexOf( event ); - - if( row != -1 ) - { - fireTableCellUpdated( row, FROM_ID ); - fireTableCellUpdated( row, FROM_ALIAS ); - } - else - { - mLog.error( "CallEventModel - tried to set from ID on " - + "call event - couldn't find index of event" ); - } - } - } - } - catch( Exception e ) - { - e.printStackTrace(); - } - } - } ); - } - private void prune() { - synchronized( mEvents ) + while( mEvents.size() > mMaxMessages ) { - while( mEvents.size() > mMaxMessages ) - { - CallEvent event = mEvents.removeLast(); - - mBroadcaster.receive( event ); - - fireTableRowsDeleted( mEvents.size(), mEvents.size() ); - } + int index = mEvents.size() - 1; + mEvents.remove( index ); + + fireTableRowsDeleted( index, index ); } } @@ -315,14 +220,4 @@ public class CallEventModel extends AbstractTableModel return null; } - - public void addListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } } diff --git a/src/controller/activity/CallEventPanel.java b/src/module/decode/event/CallEventPanel.java similarity index 74% rename from src/controller/activity/CallEventPanel.java rename to src/module/decode/event/CallEventPanel.java index 2611ad54..962a090e 100644 --- a/src/controller/activity/CallEventPanel.java +++ b/src/module/decode/event/CallEventPanel.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -45,6 +45,24 @@ public class CallEventPanel extends JPanel implements ChannelEventListener add( mEmptyScroller ); } +// if( mCallEventTable == null ) +// { +// mCallEventTable = new JTable( mCallEventModel ); +// mCallEventTable.setAutoCreateRowSorter( true ); +// mCallEventTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); +// +// CallEventAliasCellRenderer renderer = +// new CallEventAliasCellRenderer( mProcessingChain +// .getResourceManager().getSettingsManager() ); +// +// mCallEventTable.getColumnModel().getColumn( 3 ) +// .setCellRenderer( renderer ); +// +// mCallEventTable.getColumnModel().getColumn( 5 ) +// .setCellRenderer( renderer ); +// } +// +// return mCallEventTable; @Override public void channelChanged( ChannelEvent event ) @@ -58,9 +76,8 @@ public class CallEventPanel extends JPanel implements ChannelEventListener { removeAll(); - JScrollPane scroller = new JScrollPane( - event.getChannel().getProcessingChain().getChannelState() - .getCallEventTable() ); + JScrollPane scroller = new JScrollPane( new JTable( + event.getChannel().getCallEventModel() ) ); add( scroller ); @@ -70,8 +87,8 @@ public class CallEventPanel extends JPanel implements ChannelEventListener repaint(); } } - else if( event.getEvent() == Event.PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DELETED ) + else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || + event.getEvent() == Event.CHANNEL_DISABLED ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) diff --git a/src/module/decode/event/ICallEventProvider.java b/src/module/decode/event/ICallEventProvider.java new file mode 100644 index 00000000..f8645d36 --- /dev/null +++ b/src/module/decode/event/ICallEventProvider.java @@ -0,0 +1,9 @@ +package module.decode.event; + +import sample.Listener; + +public interface ICallEventProvider +{ + public void setCallEventListener( Listener listener ); + public void removeCallEventListener(); +} diff --git a/src/controller/activity/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java similarity index 97% rename from src/controller/activity/MessageActivityModel.java rename to src/module/decode/event/MessageActivityModel.java index 7ee2e987..65850196 100644 --- a/src/controller/activity/MessageActivityModel.java +++ b/src/module/decode/event/MessageActivityModel.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import java.awt.EventQueue; import java.text.SimpleDateFormat; @@ -122,7 +122,7 @@ public class MessageActivityModel extends AbstractTableModel public void receive( final Message message ) { - if( mMessageFilter.passes( message ) ) + if( message.isValid() && mMessageFilter.passes( message ) ) { EventQueue.invokeLater( new Runnable() { diff --git a/src/controller/activity/MessageActivityPanel.java b/src/module/decode/event/MessageActivityPanel.java similarity index 90% rename from src/controller/activity/MessageActivityPanel.java rename to src/module/decode/event/MessageActivityPanel.java index 6a216e1f..ae026b14 100644 --- a/src/controller/activity/MessageActivityPanel.java +++ b/src/module/decode/event/MessageActivityPanel.java @@ -15,11 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; - -import filter.FilterEditorPanel; -import filter.FilterSet; -import gui.SDRTrunk; +package module.decode.event; import java.awt.EventQueue; import java.awt.event.ActionEvent; @@ -52,12 +48,15 @@ import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import filter.FilterEditorPanel; +import filter.FilterSet; public class MessageActivityPanel extends JPanel implements ChannelEventListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); + private final static Logger mLog = + LoggerFactory.getLogger( MessageActivityPanel.class ); private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel(); @@ -92,8 +91,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { mDisplayedChannel = event.getChannel(); - mMessageTable.setModel( event.getChannel().getProcessingChain() - .getChannelState().getMessageActivityModel() ); + mMessageTable.setModel( event.getChannel().getMessageActivityModel() ); if( mDisplayedChannel != null ) { @@ -107,8 +105,8 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener changed = true; } } - else if( event.getEvent() == Event.PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DELETED ) + else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || + event.getEvent() == Event.CHANNEL_DISABLED ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) @@ -199,8 +197,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener if( mDisplayedChannel != null ) { - slider.setValue( mDisplayedChannel.getProcessingChain() - .getChannelState().getMessageActivityModel() + slider.setValue( mDisplayedChannel.getMessageActivityModel() .getMaxMessageCount() ); } else @@ -215,8 +212,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { if( mDisplayedChannel != null ) { - mDisplayedChannel.getProcessingChain().getChannelState() - .getMessageActivityModel() + mDisplayedChannel.getMessageActivityModel() .setMaxMessageCount( slider.getValue() ); } } @@ -234,8 +230,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { if( mDisplayedChannel != null ) { - mDisplayedChannel.getProcessingChain().getChannelState() - .getMessageActivityModel().clear(); + mDisplayedChannel.getMessageActivityModel().clear(); } } } ); @@ -274,9 +269,8 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener @SuppressWarnings( "unchecked" ) FilterSet filter = (FilterSet) - mDisplayedChannel - .getProcessingChain().getChannelState() - .getMessageActivityModel().getMessageFilter(); + mDisplayedChannel.getMessageActivityModel() + .getMessageFilter(); FilterEditorPanel panel = new FilterEditorPanel( filter ); diff --git a/src/controller/activity/MessageDetailsProvider.java b/src/module/decode/event/MessageDetailsProvider.java similarity index 97% rename from src/controller/activity/MessageDetailsProvider.java rename to src/module/decode/event/MessageDetailsProvider.java index 97672505..bfce825e 100644 --- a/src/controller/activity/MessageDetailsProvider.java +++ b/src/module/decode/event/MessageDetailsProvider.java @@ -15,14 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import alias.Alias; /** * Getter methods to support the Message Activity Table/Model - * @author denny - * */ public interface MessageDetailsProvider { diff --git a/src/decode/fleetsync1/Fleetsync1Message.java b/src/module/decode/fleetsync1/Fleetsync1Message.java similarity index 99% rename from src/decode/fleetsync1/Fleetsync1Message.java rename to src/module/decode/fleetsync1/Fleetsync1Message.java index 80826562..73694103 100644 --- a/src/decode/fleetsync1/Fleetsync1Message.java +++ b/src/module/decode/fleetsync1/Fleetsync1Message.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync1; +package module.decode.fleetsync1; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/fleetsync1/Fleetsync1MessageProcessor.java b/src/module/decode/fleetsync1/Fleetsync1MessageProcessor.java similarity index 98% rename from src/decode/fleetsync1/Fleetsync1MessageProcessor.java rename to src/module/decode/fleetsync1/Fleetsync1MessageProcessor.java index 8890d5a5..890bdd3d 100644 --- a/src/decode/fleetsync1/Fleetsync1MessageProcessor.java +++ b/src/module/decode/fleetsync1/Fleetsync1MessageProcessor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync1; +package module.decode.fleetsync1; import message.Message; diff --git a/src/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java similarity index 68% rename from src/decode/fleetsync2/Fleetsync2Decoder.java rename to src/module/decode/fleetsync2/Fleetsync2Decoder.java index 98369eb5..f9c3bb44 100644 --- a/src/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,37 +15,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatTap; +import instrument.tap.stream.FloatBufferTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Listener; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; /** * Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate - * complex or floating point samples and output fully framed Fleetsync II - * messages + * floating point samples and output fully framed Fleetsync II messages */ -public class Fleetsync2Decoder extends Decoder implements Instrumentable +public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int sDECIMATED_SAMPLE_RATE = 24000; @@ -58,29 +57,27 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = "Tap Point: FSK2 Decoder > < Message Framer"; private FSK2Decoder mFSKDecoder; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private MessageFramer mMessageFramer; private Fleetsync2MessageProcessor mMessageProcessor; public Fleetsync2Decoder( AliasList aliasList ) { - super( SampleType.REAL ); - - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + super( new FleetsyncDecoderState( aliasList ) ); + + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); + + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); mDecimationFilter.setListener( mBandPassFilter ); mFSKDecoder = new FSK2Decoder( sDECIMATED_SAMPLE_RATE, @@ -93,35 +90,28 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable mMessageProcessor = new Fleetsync2MessageProcessor( aliasList ); mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( this ); + + mMessageProcessor.setMessageListener( mMessageBroadcaster ); } public void dispose() { super.dispose(); - mDecimationFilter.dispose(); mBandPassFilter.dispose(); + mDecimationFilter.dispose(); + mDecoderState.dispose(); mFSKDecoder.dispose(); mMessageFramer.dispose(); mMessageProcessor.dispose(); } @Override - public DecoderType getType() - { - return DecoderType.FLEETSYNC2; - } - - /** - * Returns a float listener interface for connecting this decoder to a - * float stream provider - */ - public RealSampleListener getRealReceiver() + public DecoderType getDecoderType() { - return (RealSampleListener)mDecimationFilter; + return DecoderType.FLEETSYNC2; } - + @Override public List getTaps() { @@ -129,8 +119,7 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); - mAvailableTaps.add( new FloatTap( + mAvailableTaps.add( new FloatBufferTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); mAvailableTaps.add( new BinaryTap( @@ -147,12 +136,8 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - addRealSampleListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatTap bpTap = (FloatTap)tap; + FloatBufferTap bpTap = (FloatBufferTap)tap; mBandPassFilter.setListener( bpTap ); bpTap.setListener( mFSKDecoder ); break; @@ -171,10 +156,6 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - removeRealListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -185,15 +166,8 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } - - @Override - public IAudioOutput getAudioOutput() + public Listener getRealBufferListener() { - //Not implemented - return null; + return mDecimationFilter; } } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java b/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java new file mode 100644 index 00000000..1da74da3 --- /dev/null +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * 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 module.decode.fleetsync2; + +import java.awt.EventQueue; + +import javax.swing.JLabel; + +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import net.miginfocom.swing.MigLayout; +import settings.ColorSetting; +import settings.ColorSetting.ColorSettingName; +import settings.Setting; +import settings.SettingsManager; + +public class Fleetsync2DecoderPanel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + private static final String PROTOCOL = "FSync II"; + + private JLabel mFromLabel = new JLabel( " " ); + private JLabel mFrom = new JLabel(); + private JLabel mFromAlias = new JLabel(); + + private JLabel mToLabel = new JLabel(); + private JLabel mTo = new JLabel( " " ); + private JLabel mToAlias = new JLabel(); + + private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mMessage = new JLabel(); + private JLabel mMessageType = new JLabel(); + + public Fleetsync2DecoderPanel( SettingsManager settingsManager, + Fleetsync2Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + private FleetsyncDecoderState getState() + { + return (FleetsyncDecoderState)mDecoder.getDecoderState(); + } + + public void dispose() + { + super.dispose(); + } + + protected void init() + { + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); + + setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + mFromLabel.setFont( mFontAuxDecoder ); + add( mFromLabel ); + mFrom.setFont( mFontAuxDecoder ); + add( mFrom ); + mFromAlias.setFont( mFontAuxDecoder ); + add( mFromAlias, "wrap" ); + + mToLabel.setFont( mFontAuxDecoder ); + add( mToLabel ); + mTo.setFont( mFontAuxDecoder ); + add( mTo ); + mToAlias.setFont( mFontAuxDecoder ); + add( mToAlias, "wrap" ); + + mProtocol.setFont( mFontAuxDecoder ); + add( mProtocol ); + mMessageType.setFont( mFontAuxDecoder ); + add( mMessageType ); + mMessage.setFont( mFontAuxDecoder ); + add( mMessage, "wrap" ); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case FROM_TALKGROUP: + String from = getState().getFromID(); + + if( from != null ) + { + mFromLabel.setText( "FM:" ); + } + else + { + mFromLabel.setText( " " ); + } + mFrom.setText( from ); + break; + case FROM_TALKGROUP_ALIAS: + Alias fromAlias = getState().getFromIDAlias(); + + if( fromAlias != null ) + { + mFromAlias.setText( fromAlias.getName() ); + } + break; + case TO_TALKGROUP: + String to = getState().getToID(); + + if( to != null ) + { + mToLabel.setText( "TO:" ); + } + else + { + mToLabel.setText( " " ); + } + mTo.setText( to ); + break; + case TO_TALKGROUP_ALIAS: + Alias toAlias = getState().getToIDAlias(); + + if( toAlias != null ) + { + mToAlias.setText( toAlias.getName() ); + } + break; + case MESSAGE: + mMessage.setText( getState().getMessage() ); + break; + case MESSAGE_TYPE: + mMessageType.setText( getState().getMessageType() ); + break; + default: + break; + } + + repaint(); + } + } ); + } + + @Override + public void settingChanged( Setting setting ) + { + super.settingChanged( setting ); + + if( setting instanceof ColorSetting ) + { + ColorSetting color = (ColorSetting)setting; + + if( color.getColorSettingName() == + ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + { + mFromLabel.setForeground( color.getColor() ); + mFrom.setForeground( color.getColor() ); + mFromAlias.setForeground( color.getColor() ); + mToLabel.setForeground( color.getColor() ); + mTo.setForeground( color.getColor() ); + mToAlias.setForeground( color.getColor() ); + mProtocol.setForeground( color.getColor() ); + mMessageType.setForeground( color.getColor() ); + mMessage.setForeground( color.getColor() ); + } + } + } +} diff --git a/src/decode/fleetsync2/Fleetsync2MessageProcessor.java b/src/module/decode/fleetsync2/Fleetsync2MessageProcessor.java similarity index 77% rename from src/decode/fleetsync2/Fleetsync2MessageProcessor.java rename to src/module/decode/fleetsync2/Fleetsync2MessageProcessor.java index 5bf85d02..23c74653 100644 --- a/src/decode/fleetsync2/Fleetsync2MessageProcessor.java +++ b/src/module/decode/fleetsync2/Fleetsync2MessageProcessor.java @@ -15,17 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; import message.Message; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; public class Fleetsync2MessageProcessor implements Listener { - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -36,7 +35,8 @@ public class Fleetsync2MessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; + mAliasList = null; } @Override @@ -44,16 +44,19 @@ public class Fleetsync2MessageProcessor implements Listener { FleetsyncMessage message = new FleetsyncMessage( buffer, mAliasList ); - mBroadcaster.receive( message ); + if( mMessageListener != null ) + { + mMessageListener.receive( message ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/fleetsync2/FleetsyncCallEvent.java b/src/module/decode/fleetsync2/FleetsyncCallEvent.java similarity index 98% rename from src/decode/fleetsync2/FleetsyncCallEvent.java rename to src/module/decode/fleetsync2/FleetsyncCallEvent.java index 1c7dd85e..4b7b2121 100644 --- a/src/decode/fleetsync2/FleetsyncCallEvent.java +++ b/src/module/decode/fleetsync2/FleetsyncCallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class FleetsyncCallEvent extends CallEvent { diff --git a/src/module/decode/fleetsync2/FleetsyncDecoderState.java b/src/module/decode/fleetsync2/FleetsyncDecoderState.java new file mode 100644 index 00000000..ae40ecb0 --- /dev/null +++ b/src/module/decode/fleetsync2/FleetsyncDecoderState.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * 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 module.decode.fleetsync2; + +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class FleetsyncDecoderState extends DecoderState +{ + private TreeSet mIdents = new TreeSet(); + private TreeSet mEmergencyIdents = new TreeSet(); + + private String mFrom; + private Alias mFromAlias; + + private String mTo; + private Alias mToAlias; + + private String mMessage; + private String mMessageType; + + public FleetsyncDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void dispose() + { + super.dispose(); + + + } + + @Override + public void init() + { + + } + + /** + * Resets the overall decoder state and clears any accumulated event details + */ + @Override + public void reset() + { + mIdents.clear(); + mEmergencyIdents.clear(); + + resetState(); + } + + /** + * Resets this decoder state + */ + public void resetState() + { + setFromID( null ); + setFromIDAlias( null ); + setToID( null ); + setToIDAlias( null ); + setMessage( null ); + setMessageType( null ); + } + + @Override + public void receive( Message message ) + { + if( message instanceof FleetsyncMessage ) + { + FleetsyncMessage fleetsync = (FleetsyncMessage)message; + + if( fleetsync.isValid() ) + { + setFromID( fleetsync.getFromID() ); + setFromIDAlias( fleetsync.getFromIDAlias() ); + mIdents.add( fleetsync.getFromID() ); + + FleetsyncMessageType type = fleetsync.getMessageType(); + + if( type != FleetsyncMessageType.ANI ) + { + setToID( fleetsync.getToID() ); + setToIDAlias( fleetsync.getToIDAlias() ); + mIdents.add( fleetsync.getToID() ); + } + + setMessageType( type.getLabel() ); + + switch( type ) + { + case GPS: + setMessage( fleetsync.getGPSLocation() ); + 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() ); + break; + case EMERGENCY: + case LONE_WORKER_EMERGENCY: + mEmergencyIdents.add( fleetsync.getFromID() ); + break; + default: + break; + } + + FleetsyncCallEvent fsCallEvent = + FleetsyncCallEvent.getFleetsync2Event( fleetsync ); + + fsCallEvent.setAliasList( getAliasList() ); + + broadcast( fsCallEvent ); + + /* Broadcast decode event. If the primary state is not already + * in a call state, then we'll use a default decoded data state */ + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + } + } + } + + public String getFromID() + { + return mFrom; + } + + public void setFromID( String from ) + { + mFrom = from; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromIDAlias() + { + return mFromAlias; + } + + public void setFromIDAlias( Alias alias ) + { + mFromAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getToID() + { + return mTo; + } + + public void setToID( String to ) + { + mTo = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToIDAlias() + { + return mToAlias; + } + + public void setToIDAlias( Alias alias ) + { + mToAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage( String message ) + { + mMessage = message; + broadcast( ChangedAttribute.MESSAGE ); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType( String type ) + { + mMessageType = type; + broadcast( ChangedAttribute.MESSAGE_TYPE ); + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + default: + break; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tFleetsync II\n\n" ); + sb.append( "Fleetsync Idents\n" ); + + if( mIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + 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" ); + } + } + } + + 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/decode/fleetsync2/FleetsyncMessage.java b/src/module/decode/fleetsync2/FleetsyncMessage.java similarity index 99% rename from src/decode/fleetsync2/FleetsyncMessage.java rename to src/module/decode/fleetsync2/FleetsyncMessage.java index 190e0a1c..85a47cf7 100644 --- a/src/decode/fleetsync2/FleetsyncMessage.java +++ b/src/module/decode/fleetsync2/FleetsyncMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; import java.text.DecimalFormat; import java.text.SimpleDateFormat; diff --git a/src/decode/fleetsync2/FleetsyncMessageFilter.java b/src/module/decode/fleetsync2/FleetsyncMessageFilter.java similarity index 97% rename from src/decode/fleetsync2/FleetsyncMessageFilter.java rename to src/module/decode/fleetsync2/FleetsyncMessageFilter.java index 8c04a308..a9a61c5f 100644 --- a/src/decode/fleetsync2/FleetsyncMessageFilter.java +++ b/src/module/decode/fleetsync2/FleetsyncMessageFilter.java @@ -1,4 +1,4 @@ -package decode.fleetsync2; +package module.decode.fleetsync2; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/fleetsync2/FleetsyncMessageType.java b/src/module/decode/fleetsync2/FleetsyncMessageType.java similarity index 97% rename from src/decode/fleetsync2/FleetsyncMessageType.java rename to src/module/decode/fleetsync2/FleetsyncMessageType.java index 1d3a09a5..6f3730b0 100644 --- a/src/decode/fleetsync2/FleetsyncMessageType.java +++ b/src/module/decode/fleetsync2/FleetsyncMessageType.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; public enum FleetsyncMessageType { diff --git a/src/decode/lj1200/LJ1200CallEvent.java b/src/module/decode/lj1200/LJ1200CallEvent.java similarity index 95% rename from src/decode/lj1200/LJ1200CallEvent.java rename to src/module/decode/lj1200/LJ1200CallEvent.java index a6f52608..5be56feb 100644 --- a/src/decode/lj1200/LJ1200CallEvent.java +++ b/src/module/decode/lj1200/LJ1200CallEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class LJ1200CallEvent extends CallEvent { diff --git a/src/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java similarity index 76% rename from src/decode/lj1200/LJ1200Decoder.java rename to src/module/decode/lj1200/LJ1200Decoder.java index cf4367b8..2454657c 100644 --- a/src/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,35 +15,38 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Broadcaster; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import sample.real.RealSampleListener; -import source.Source.SampleType; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; /** * LJ1200 - 1200 baud 2FSK decoder */ -public class LJ1200Decoder extends Decoder implements Instrumentable +public class LJ1200Decoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int DECIMATED_SAMPLE_RATE = 24000; @@ -56,8 +59,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = @@ -67,22 +68,21 @@ public class LJ1200Decoder extends Decoder implements Instrumentable private Broadcaster mFSKDecoderBroadcaster = new Broadcaster(); - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private MessageFramer mTowerMessageFramer; private MessageFramer mTransponderMessageFramer; private LJ1200MessageProcessor mMessageProcessor; public LJ1200Decoder( AliasList aliasList ) { - super( SampleType.REAL ); + super( new LJ1200DecoderState( aliasList ) ); - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); mDecimationFilter.setListener( mBandPassFilter ); mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE, @@ -101,10 +101,16 @@ public class LJ1200Decoder extends Decoder implements Instrumentable mMessageProcessor = new LJ1200MessageProcessor( aliasList ); mTowerMessageFramer.addMessageListener( mMessageProcessor ); mTransponderMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( this ); + mMessageProcessor.setMessageListener( mMessageBroadcaster ); } - public void dispose() + @Override + public DecoderType getDecoderType() + { + return DecoderType.LJ_1200; + } + + public void dispose() { super.dispose(); @@ -115,12 +121,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable mMessageProcessor.dispose(); } - @Override - public DecoderType getType() - { - return DecoderType.FLEETSYNC2; - } - /** * Returns a float listener interface for connecting this decoder to a * float stream provider @@ -137,7 +137,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); @@ -155,12 +154,8 @@ public class LJ1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - addRealSampleListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatTap bpTap = (FloatTap)tap; + FloatBufferTap bpTap = (FloatBufferTap)tap; mBandPassFilter.setListener( bpTap ); bpTap.setListener( mFSKDecoder ); break; @@ -179,10 +174,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - removeRealListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -193,15 +184,8 @@ public class LJ1200Decoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } - - @Override - public IAudioOutput getAudioOutput() + public Listener getRealBufferListener() { - //Not implemented - return null; + return mDecimationFilter; } } diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java new file mode 100644 index 00000000..3c3ab3c6 --- /dev/null +++ b/src/module/decode/lj1200/LJ1200DecoderState.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * 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 module.decode.lj1200; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class LJ1200DecoderState extends DecoderState +{ + private Set mAddresses = new TreeSet(); + + private String mAddress; + private Alias mAddressAlias; + + public LJ1200DecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + public String getAddress() + { + return mAddress; + } + + public Alias getAddressAlias() + { + return mAddressAlias; + } + + @Override + public void receive( Message message ) + { + if( message instanceof LJ1200Message ) + { + LJ1200Message lj = (LJ1200Message)message; + + if( lj.isValid() ) + { + setAddress( lj.getAddress() ); + setAddressAlias( lj.getAddressAlias() ); + + mAddresses.add( mAddress ); + + broadcast( LJ1200CallEvent.getLJ1200Event( lj ) ); + + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + } + } + else if( message instanceof LJ1200TransponderMessage ) + { + + } + } + + private void setAddress( String address ) + { + mAddress = address; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + private void setAddressAlias( Alias alias ) + { + mAddressAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tLJ-1200I\n\n" ); + + if( !mAddresses.isEmpty() ) + { + sb.append( "Transponder Addresses:\n" ); + + List addresses = new ArrayList( mAddresses ); + + Collections.sort( addresses ); + + for( String address: addresses ) + { + sb.append( "\t" ); + sb.append( address ); + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization steps */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + if( event.getEvent() == Event.RESET ) + { + + } + } + + @Override + public void reset() + { + mAddresses.clear(); + + setAddress( null ); + setAddressAlias( null ); + } +} diff --git a/src/decode/lj1200/LJ1200Message.java b/src/module/decode/lj1200/LJ1200Message.java similarity index 97% rename from src/decode/lj1200/LJ1200Message.java rename to src/module/decode/lj1200/LJ1200Message.java index 385ac24f..d174e2f2 100644 --- a/src/decode/lj1200/LJ1200Message.java +++ b/src/module/decode/lj1200/LJ1200Message.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -320,12 +320,12 @@ public class LJ1200Message extends Message F4_ACTIVATE( "4-ACTIVATE" ), F5_TRANSPONDER_TEST_REPLY( "5-TRANSPONDER TEST REPLY" ), F6_TRANSPONDER_TEST_COMMAND( "6-TRANSPONDER TEST COMMAND" ), - F7_UNKNOWN( "7-UNKNOWN" ), + F7_QUIET_COMMAND( "7-QUIET COMMAND" ), F8_UNKNOWN( "8-UNKNOWN" ), F9_UNKNOWN( "9-UNKNOWN" ), FA_UNKNOWN( "A-UNKNOWN" ), FB_UNKNOWN( "B-UNKNOWN" ), - FC_UNKNOWN( "C-UNKNOWN" ), + FC_ALT_DEACTIVATE( "C-ALT-DEACTIVATE" ), FD_UNKNOWN( "D-UNKNOWN" ), FE_UNKNOWN( "E-UNKNOWN" ), FF_TRACK_PULSE( "F-TRACK PULSE" ), @@ -375,7 +375,7 @@ public class LJ1200Message extends Message case 6: return Function.F6_TRANSPONDER_TEST_COMMAND; case 7: - return Function.F7_UNKNOWN; + return Function.F7_QUIET_COMMAND; case 8: return Function.F8_UNKNOWN; case 9: @@ -385,7 +385,7 @@ public class LJ1200Message extends Message case 11: return Function.FB_UNKNOWN; case 12: - return Function.FC_UNKNOWN; + return Function.FC_ALT_DEACTIVATE; case 13: return Function.FD_UNKNOWN; case 14: diff --git a/src/module/decode/lj1200/LJ1200MessageFilter.java b/src/module/decode/lj1200/LJ1200MessageFilter.java new file mode 100644 index 00000000..7f6257d7 --- /dev/null +++ b/src/module/decode/lj1200/LJ1200MessageFilter.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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 module.decode.lj1200; + +import java.util.Collections; +import java.util.List; + +import message.Message; +import filter.Filter; +import filter.FilterElement; + +public class LJ1200MessageFilter extends Filter +{ + + public LJ1200MessageFilter() + { + super( "LJ-1200 Message Filter" ); + } + + @Override + public boolean passes( Message message ) + { + return mEnabled && canProcess( message ); + } + + @Override + public boolean canProcess( Message message ) + { + return message instanceof LJ1200Message || + message instanceof LJ1200TransponderMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/decode/lj1200/LJ1200MessageProcessor.java b/src/module/decode/lj1200/LJ1200MessageProcessor.java similarity index 71% rename from src/decode/lj1200/LJ1200MessageProcessor.java rename to src/module/decode/lj1200/LJ1200MessageProcessor.java index 0d4378f2..fc11279c 100644 --- a/src/decode/lj1200/LJ1200MessageProcessor.java +++ b/src/module/decode/lj1200/LJ1200MessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,14 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -37,7 +36,7 @@ public class LJ1200MessageProcessor implements Listener public static int SYNC_TOWER = 0x550F; public static int SYNC_TRANSPONDER = 0x2AD5; - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -48,7 +47,8 @@ public class LJ1200MessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mAliasList = null; + mMessageListener = null; } @Override @@ -58,25 +58,27 @@ public class LJ1200MessageProcessor implements Listener if( sync == SYNC_TOWER ) { - LJ1200Message towerMessage = new LJ1200Message( buffer, mAliasList ); - mBroadcaster.receive( towerMessage ); + if( mMessageListener != null ) + { + mMessageListener.receive( new LJ1200Message( buffer, mAliasList ) ); + } } else if( sync == SYNC_TRANSPONDER ) { - LJ1200TransponderMessage transponderMessage = - new LJ1200TransponderMessage( buffer, mAliasList ); - - mBroadcaster.receive( transponderMessage ); + if( mMessageListener != null ) + { + mMessageListener.receive( new LJ1200TransponderMessage( buffer, mAliasList ) ); + } } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/module/decode/lj1200/LJ1200Panel.java b/src/module/decode/lj1200/LJ1200Panel.java new file mode 100644 index 00000000..49e76030 --- /dev/null +++ b/src/module/decode/lj1200/LJ1200Panel.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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 module.decode.lj1200; + +import javax.swing.JLabel; + +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import net.miginfocom.swing.MigLayout; +import settings.ColorSetting; +import settings.ColorSetting.ColorSettingName; +import settings.Setting; +import settings.SettingsManager; + +public class LJ1200Panel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + private static final String PROTOCOL = "LJ-1200"; + + private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mTo = new JLabel(); + private JLabel mToAlias = new JLabel(); + + public LJ1200Panel( SettingsManager settingsManager, LJ1200Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + public void dispose() + { + super.dispose(); + } + + protected void init() + { + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); + + setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "" ) ); + + add( mProtocol ); + add( mTo ); + add( mToAlias, "wrap" ); + } + + private LJ1200DecoderState getState() + { + return (LJ1200DecoderState)getDecoder().getDecoderState(); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + switch( changedAttribute ) + { + case TO_TALKGROUP: + mTo.setText( getState().getAddress() ); + repaint(); + break; + case FROM_TALKGROUP_ALIAS: + Alias alias = getState().getAddressAlias(); + + if( alias != null ) + { + mToAlias.setText( alias.getName() ); + repaint(); + } + break; + default: + break; + } + } + + @Override + public void settingChanged( Setting setting ) + { + super.settingChanged( setting ); + + if( setting instanceof ColorSetting ) + { + ColorSetting color = (ColorSetting)setting; + + if( color.getColorSettingName() == + ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + { + mProtocol.setForeground( color.getColor() ); + mTo.setForeground( color.getColor() ); + mToAlias.setForeground( color.getColor() ); + } + } + } +} diff --git a/src/decode/lj1200/LJ1200TransponderMessage.java b/src/module/decode/lj1200/LJ1200TransponderMessage.java similarity index 98% rename from src/decode/lj1200/LJ1200TransponderMessage.java rename to src/module/decode/lj1200/LJ1200TransponderMessage.java index 9ba9b7c8..fecfc8fd 100644 --- a/src/decode/lj1200/LJ1200TransponderMessage.java +++ b/src/module/decode/lj1200/LJ1200TransponderMessage.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import java.text.SimpleDateFormat; diff --git a/src/decode/config/DecodeConfigLTRNet.java b/src/module/decode/ltrnet/DecodeConfigLTRNet.java similarity index 92% rename from src/decode/config/DecodeConfigLTRNet.java rename to src/module/decode/ltrnet/DecodeConfigLTRNet.java index 6c62e6ff..574785b5 100644 --- a/src/decode/config/DecodeConfigLTRNet.java +++ b/src/module/decode/ltrnet/DecodeConfigLTRNet.java @@ -15,12 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.ltrnet; import javax.xml.bind.annotation.XmlAttribute; import message.MessageDirection; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigLTRNet extends DecodeConfiguration { diff --git a/src/decode/ltrnet/LTRCallEvent.java b/src/module/decode/ltrnet/LTRCallEvent.java similarity index 89% rename from src/decode/ltrnet/LTRCallEvent.java rename to src/module/decode/ltrnet/LTRCallEvent.java index 3368d26d..bfd1b8ed 100644 --- a/src/decode/ltrnet/LTRCallEvent.java +++ b/src/module/decode/ltrnet/LTRCallEvent.java @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; +import module.decode.DecoderType; +import module.decode.event.CallEvent; +import module.decode.ltrstandard.LTRStandardMessage; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; -import decode.ltrstandard.LTRStandardMessage; public class LTRCallEvent extends CallEvent { @@ -64,11 +64,19 @@ public class LTRCallEvent extends CallEvent return false; } - public boolean addMessage( LTRNetMessage message ) + /** + * 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.contentEquals( getToID() ) ) + if( talkgroup != null && getToID() != null && + talkgroup.contentEquals( getToID() ) ) { mValidCallMessages++; diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java new file mode 100644 index 00000000..47636daa --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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 module.decode.ltrnet; + +import instrument.Instrumentable; +import instrument.tap.Tap; + +import java.util.ArrayList; +import java.util.List; + +import message.MessageDirection; +import module.decode.Decoder; +import module.decode.DecoderType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import alias.AliasList; +import bits.MessageFramer; +import bits.SyncPattern; +import dsp.fsk.LTRFSKDecoder; + +public class LTRNetDecoder extends Decoder + implements IRealBufferListener, Instrumentable +{ + public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; + private LTRFSKDecoder mLTRFSKDecoder; + private MessageFramer mLTRMessageFramer; + private LTRNetMessageProcessor mLTRMessageProcessor; + + private List mAvailableTaps; + + public LTRNetDecoder( DecodeConfigLTRNet config, AliasList aliasList ) + { + super( new LTRNetDecoderState( aliasList ) ); + + mLTRFSKDecoder = new LTRFSKDecoder(); + + + if( config.getMessageDirection() == MessageDirection.OSW ) + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + else + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + + mLTRFSKDecoder.addListener( mLTRMessageFramer ); + + mLTRMessageProcessor = new LTRNetMessageProcessor( + config.getMessageDirection(), aliasList ); + + mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); + + mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.LTR_NET; + } + + @Override + public Listener getRealBufferListener() + { + return mLTRFSKDecoder; + } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.addAll( mLTRFSKDecoder.getTaps() ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + mLTRFSKDecoder.addTap( tap ); + } + + @Override + public void removeTap( Tap tap ) + { + mLTRFSKDecoder.removeTap( tap ); + } +} diff --git a/src/decode/ltrnet/LTRNetPanel.java b/src/module/decode/ltrnet/LTRNetDecoderPanel.java similarity index 66% rename from src/decode/ltrnet/LTRNetPanel.java rename to src/module/decode/ltrnet/LTRNetDecoderPanel.java index 276acf48..3f4e4a8d 100644 --- a/src/decode/ltrnet/LTRNetPanel.java +++ b/src/module/decode/ltrnet/LTRNetDecoderPanel.java @@ -15,27 +15,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.awt.EventQueue; import javax.swing.JLabel; +import net.miginfocom.swing.MigLayout; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; +import settings.SettingsManager; import alias.Alias; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; -public class LTRNetPanel extends ChannelStatePanel +public class LTRNetDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol = new JLabel( "LTR-Net" ); private JLabel mTalkgroup = new JLabel(); private JLabel mTalkgroupAlias = new JLabel(); @@ -43,28 +40,19 @@ public class LTRNetPanel extends ChannelStatePanel private JLabel mLCN = new JLabel( "LCN:" ); private JLabel mDescription = new JLabel(); - public LTRNetPanel( Channel channel ) + public LTRNetDecoderPanel( SettingsManager settingsManager, LTRNetDecoder decoder ) { - super( channel ); + super( settingsManager, decoder ); init(); } public void init() { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); + super.init(); - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); @@ -80,10 +68,6 @@ public class LTRNetPanel extends ChannelStatePanel mDescription.setFont( mFontDecoder ); mDescription.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mTalkgroup ); add( mTalkgroupAlias, "wrap" ); @@ -91,6 +75,11 @@ public class LTRNetPanel extends ChannelStatePanel add( mLCN ); add( mDescription, "span,wrap" ); } + + private LTRNetDecoderState getDecoderState() + { + return (LTRNetDecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute changedAttribute ) @@ -100,23 +89,10 @@ public class LTRNetPanel extends ChannelStatePanel @Override public void run() { - LTRNetChannelState state = (LTRNetChannelState)mChannel - .getProcessingChain().getChannelState(); + LTRNetDecoderState state = getDecoderState(); switch( changedAttribute ) { - case CHANNEL_STATE: - mStateLabel.setText( state.getState().getDisplayValue() ); - break; - case SOURCE: - mSourceLabel.setText( mChannel - .getSourceConfiguration().getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; case TO_TALKGROUP: mTalkgroup.setText( state.getToTalkgroup() ); break; @@ -127,7 +103,7 @@ public class LTRNetPanel extends ChannelStatePanel { mTalkgroupAlias.setText( tgAlias.getName() ); String iconNameString = tgAlias.getIconName(); - mTalkgroupAlias.setIcon( getSettingsManager() + mTalkgroupAlias.setIcon( mSettingsManager .getImageIcon( iconNameString, 12 ) ); } else @@ -163,10 +139,6 @@ public class LTRNetPanel extends ChannelStatePanel switch( colorSetting.getColorSettingName() ) { case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); @@ -188,16 +160,6 @@ public class LTRNetPanel extends ChannelStatePanel mDescription.setForeground( mColorLabelDecoder ); } break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - break; } } } diff --git a/src/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java new file mode 100644 index 00000000..cecfb04d --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetDecoderState.java @@ -0,0 +1,707 @@ +/******************************************************************************* + * 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 module.decode.ltrnet; + +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class LTRNetDecoderState extends DecoderState +{ + private DecimalFormat mDecimalFormatter = new DecimalFormat( "0.00000" ); + + 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 String mTalkgroup; + private Alias mTalkgroupAlias; + private String mDescription; + private int mChannelNumber; + private long mFrequency = 0; + + public LTRNetDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void receive( Message message ) + { + if( message.isValid() ) + { + if( message instanceof LTRNetOSWMessage ) + { + LTRNetOSWMessage ltr = (LTRNetOSWMessage)message; + + switch( ltr.getMessageType() ) + { + case CA_STRT: + case CA_ENDD: + if( mChannelNumber == 0 ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + if( ltr.getChannel() == 31 ) + { + processCallEndMessage( ltr ); + } + else if( ltr.getChannel() == mChannelNumber ) + { + processCallMessage( ltr ); + } + else + { + processCallDetectMessage( ltr ); + } + break; + case SY_IDLE: + if( mChannelNumber != ltr.getChannel() ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + 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: + int uniqueID = ltr.getRadioUniqueID(); + + if( uniqueID != LTRNetOSWMessage.INT_NULL_VALUE ) + { + mUniqueIDs.add( uniqueID ); + } + + if( getCurrentLTRCallEvent() == null ) + { + mCurrentCallEvent = new LTRCallEvent.Builder( 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 ); + } + + mTalkgroup = String.valueOf( uniqueID ); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + break; + case ID_SITE: + String siteID = ltr.getSiteID(); + + if( siteID != null ) + { + mSiteIDs.add( siteID ); + } + break; + default: + break; + } + } + else if( message instanceof LTRNetISWMessage ) + { + 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: + String esn = ltr.getESN(); + + if( !esn.contains( "xxxx" ) ) + { + mESNs.add( ltr.getESN() ); + } + + mDescription = "ESN"; + broadcast( ChangedAttribute.DESCRIPTION ); + + mTalkgroup = ltr.getESN(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getESNAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + + if( mCurrentCallEvent == null ) + { + mCurrentCallEvent = new LTRCallEvent.Builder( CallEventType.REGISTER_ESN ) + .aliasList( getAliasList() ) + .details( "ESN:" + ltr.getESN() ) + .frequency( mFrequency ) + .from( ltr.getESN() ) + .build(); + + broadcast( mCurrentCallEvent ); + } + + break; + case ID_UNIQ: + int uniqueid = ltr.getRadioUniqueID(); + + if( uniqueid != LTRNetISWMessage.INT_NULL_VALUE ) + { + mUniqueIDs.add( uniqueid ); + + mDescription = "REGISTER UID"; + broadcast( ChangedAttribute.DESCRIPTION ); + + mTalkgroup = String.valueOf( uniqueid ); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + if( getCurrentLTRCallEvent() == null ) + { + mCurrentCallEvent = new LTRCallEvent.Builder( 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; + } + } + } + } + + public LTRCallEvent getCurrentLTRCallEvent() + { + if( mCurrentCallEvent != null ) + { + return (LTRCallEvent)mCurrentCallEvent; + } + + return null; + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tLTR-Net\n\n" ); + + if( mSiteIDs.isEmpty() ) + { + sb.append( "Site:\tUnknown\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( "\nLCNs (transmit | receive)\n" ); + + if( mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + for( int x = 1; x < 21; x++ ) + { + 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( "\nTalkgroups\n" ); + + if( mTalkgroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + 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( "\nRadio Unique IDs\n" ); + + if( mUniqueIDs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mUniqueIDs.iterator(); + + while( it.hasNext() ) + { + 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( "\nESNs\n" ); + + if( mESNs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mESNs.iterator(); + + while( it.hasNext() ) + { + 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" ); + } + } + + 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( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( String.valueOf( message.getChannel() ) ) + .frequency( frequency ) + .to( message.getTalkgroupID() ) + .build() ); + } + } + + private void processCallMessage( LTRNetMessage message ) + { + LTRCallEvent current = getCurrentLTRCallEvent(); + + int group = message.getGroup(); + + /* If this is a new call or the talkgroup is different from the current + * call, create a new call event */ + if( current == null || !current.isMatchingTalkgroup( message ) ) + { + /* Invalidate the current call */ + if( current != null ) + { + current.setValid( false ); + broadcast( current ); + } + + mTalkgroup = message.getTalkgroupID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + /* 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( mTalkgroup ) ) + { + mTalkgroups.add( mTalkgroup ); + } + else + { + mTalkgroupsFirstHeard.add( mTalkgroup ); + } + + mTalkgroupAlias = message.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + CallEventType type = CallEventType.CALL; + + if( group == 253 ) + { + type = CallEventType.REGISTER; + + mDescription = "REGISTER"; + broadcast( ChangedAttribute.DESCRIPTION ); + } + else if( group == 254 ) + { + type = CallEventType.STATION_ID; + + mDescription = "STATION ID"; + broadcast( ChangedAttribute.DESCRIPTION ); + } + + mCurrentCallEvent = new LTRCallEvent.Builder( type ) + .aliasList( getAliasList() ) + .channel( String.valueOf( message.getChannel() ) ) + .frequency( mFrequency ) + .to( message.getTalkgroupID() ) + .build(); + + broadcast( mCurrentCallEvent ); + + broadcast( new DecoderStateEvent( this, Event.START, + ( type == CallEventType.CALL ) ? State.CALL : State.DATA ) ); + } + else + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, + ( group == 253 || group == 254 ) ? State.DATA : State.CALL ) ); + } + } + + private void processCallEndMessage( LTRNetMessage message ) + { + if( mCurrentCallEvent != null && + mCurrentCallEvent.getCallEventType() == CallEventType.CALL ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + + } + + mCurrentCallEvent = null; + + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + } + + /** + * Resets the decoder state for re-use on another channel + */ + public void reset() + { + mChannelNumber = 0; + + mActiveCalls.clear(); + mESNs.clear(); + mNeighborIDs.clear(); + mReceiveFrequencies.clear(); + mSiteIDs.clear(); + mTalkgroups.clear(); + mTransmitFrequencies.clear(); + mUniqueIDs.clear(); + + resetState(); + } + + /** + * Resets the decoder state after a call event + */ + private void resetState() + { + if( mCurrentCallEvent != null && mCurrentCallEvent + .getCallEventType() == CallEventType.CALL ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + } + + mCurrentCallEvent = null; + + mTalkgroup = null; + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = null; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + mDescription = null; + broadcast( ChangedAttribute.DESCRIPTION ); + } + + public String getToTalkgroup() + { + return mTalkgroup; + } + + public Alias getToTalkgroupAlias() + { + return mTalkgroupAlias; + } + + public String getDescription() + { + return mDescription; + } + + public boolean hasChannelNumber() + { + return mChannelNumber != 0; + } + + public int getChannelNumber() + { + return mChannelNumber; + } + + @Override + public void init() + { + /* No initialization steps required */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = event.getFrequency(); + break; + default: + break; + } + } +} diff --git a/src/decode/ltrnet/LTRNetEditor.java b/src/module/decode/ltrnet/LTRNetEditor.java similarity index 90% rename from src/decode/ltrnet/LTRNetEditor.java rename to src/module/decode/ltrnet/LTRNetEditor.java index 1c3dc760..f35bb2ce 100644 --- a/src/decode/ltrnet/LTRNetEditor.java +++ b/src/module/decode/ltrnet/LTRNetEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -23,11 +23,11 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JCheckBox; import javax.swing.JComboBox; +import javax.swing.JLabel; import message.MessageDirection; -import decode.DecodeEditor; -import decode.config.DecodeConfigLTRNet; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class LTRNetEditor extends DecodeEditor { @@ -66,6 +66,7 @@ public class LTRNetEditor extends DecodeEditor } } ); - add( mComboDirection, "span 2,wrap" ); + add( new JLabel( "Format:" ) ); + add( mComboDirection, "wrap" ); } } diff --git a/src/decode/ltrnet/LTRNetISWMessage.java b/src/module/decode/ltrnet/LTRNetISWMessage.java similarity index 99% rename from src/decode/ltrnet/LTRNetISWMessage.java rename to src/module/decode/ltrnet/LTRNetISWMessage.java index d643bc4e..2567b5d1 100644 --- a/src/decode/ltrnet/LTRNetISWMessage.java +++ b/src/module/decode/ltrnet/LTRNetISWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.util.ArrayList; import java.util.Date; @@ -221,7 +221,7 @@ public class LTRNetISWMessage extends LTRNetMessage public int getRadioUniqueID() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.ID_UNIQ ) { diff --git a/src/decode/ltrnet/LTRNetMessage.java b/src/module/decode/ltrnet/LTRNetMessage.java similarity index 98% rename from src/decode/ltrnet/LTRNetMessage.java rename to src/module/decode/ltrnet/LTRNetMessage.java index 05f57150..75d3d468 100644 --- a/src/decode/ltrnet/LTRNetMessage.java +++ b/src/module/decode/ltrnet/LTRNetMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.text.DecimalFormat; import java.text.SimpleDateFormat; @@ -23,17 +23,17 @@ import java.text.SimpleDateFormat; import message.Message; import message.MessageDirection; import message.MessageType; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.DecoderType; import edac.CRC; import edac.CRCLTR; public abstract class LTRNetMessage extends Message { protected static final String sUNKNOWN = "**UNKNOWN**"; - protected static final int sINT_NULL_VALUE = -1; + protected static final int INT_NULL_VALUE = -1; protected static final double sDOUBLE_NULL_VALUE = -1.0D; protected SimpleDateFormat mDatestampFormatter = new SimpleDateFormat( "yyyyMMdd HHmmss" ); diff --git a/src/decode/ltrnet/LTRNetMessageFilter.java b/src/module/decode/ltrnet/LTRNetMessageFilter.java similarity index 98% rename from src/decode/ltrnet/LTRNetMessageFilter.java rename to src/module/decode/ltrnet/LTRNetMessageFilter.java index 1f5a0f02..3d73884b 100644 --- a/src/decode/ltrnet/LTRNetMessageFilter.java +++ b/src/module/decode/ltrnet/LTRNetMessageFilter.java @@ -1,4 +1,4 @@ -package decode.ltrnet; +package module.decode.ltrnet; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/module/decode/ltrnet/LTRNetMessageProcessor.java b/src/module/decode/ltrnet/LTRNetMessageProcessor.java new file mode 100644 index 00000000..bd7aa512 --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetMessageProcessor.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * 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 module.decode.ltrnet; + +import java.util.HashMap; + +import message.Message; +import message.MessageDirection; +import message.MessageType; +import sample.Listener; +import alias.AliasList; +import bits.BinaryMessage; + +public class LTRNetMessageProcessor implements Listener +{ + private Listener mMessageListener; + + private HashMap mReceiveHighAuxMessages = + new HashMap(); + private HashMap mReceiveLowAuxMessages = + new HashMap(); + + private HashMap mTransmitHighAuxMessages = + new HashMap(); + private HashMap mTransmitLowAuxMessages = + new HashMap(); + + 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 ) + { + 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 ); + } + } + + public void setMessageListener( Listener listener ) + { + mMessageListener = listener; + } + + public void removeMessageListener() + { + mMessageListener = null; + } +} diff --git a/src/decode/ltrnet/LTRNetOSWMessage.java b/src/module/decode/ltrnet/LTRNetOSWMessage.java similarity index 83% rename from src/decode/ltrnet/LTRNetOSWMessage.java rename to src/module/decode/ltrnet/LTRNetOSWMessage.java index 93e276da..5e90fd99 100644 --- a/src/decode/ltrnet/LTRNetOSWMessage.java +++ b/src/module/decode/ltrnet/LTRNetOSWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.util.ArrayList; import java.util.Date; @@ -30,27 +30,27 @@ import bits.BinaryMessage; public class LTRNetOSWMessage extends LTRNetMessage { - private static final int sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20; - private static final int sCHANNEL_MAP_MESSAGE_TYPE_BIT = 17; + private static final int CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20; + private static final int CHANNEL_MAP_MESSAGE_TYPE_BIT = 17; - private static final int sCHIU_UNIQUE_ID = 17; - private static final int sCHIU_SITE_ID = 18; - private static final int sCHIU_TRANSMIT_FREQUENCY = 24; - private static final int sCHIU_RECEIVE_FREQUENCY = 25; - private static final int sCHIU_NEIGHBOR = 26; - private static final int sCHIU_MAP = 28; - private static final int sCHIU_CALL_END = 31; + 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 sHOME_DIRECTED_GROUP_CALL = 29; - private static final int sHOME_CHANNEL_IN_USE_MUTE_AUDIO = 30; + private static final int HOME_DIRECTED_GROUP_CALL = 29; + private static final int HOME_CHANNEL_IN_USE_MUTE_AUDIO = 30; - private static final int sGROUP_DO_NOTHING = 253; - private static final int sGROUP_CWID = 254; - private static final int sGROUP_CHANNEL_IDLE = 255; + 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 sFREE_ALL_CHANNELS_BUSY = 0; - private static final int sFREE_LTR_GROUP_CALL = 30; - private static final int sFREE_LTRNET_GROUP_CALL = 31; + 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; @@ -74,7 +74,7 @@ public class LTRNetOSWMessage extends LTRNetMessage int radioId = getRadioUniqueID(); - if( radioId != sINT_NULL_VALUE ) + if( radioId != INT_NULL_VALUE ) { sb.append( " RADIO UNIQUE ID: " ); sb.append( radioId ); @@ -154,10 +154,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN RX LOW LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double rxFreqLo = getFrequency(); - if( rxFreqLo != sDOUBLE_NULL_VALUE ) + long rxFreqLo = getFrequency(); + if( rxFreqLo > 0 ) { - sb.append( mDecimalFormatter.format( rxFreqLo ) ); + sb.append( mDecimalFormatter.format( (double)rxFreqLo / 1E6d ) ); } else { @@ -169,10 +169,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN RX HIGH LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double rxfreqhi = getFrequency(); - if( rxfreqhi != sDOUBLE_NULL_VALUE ) + long rxfreqhi = getFrequency(); + if( rxfreqhi > 0 ) { - sb.append( mDecimalFormatter.format( rxfreqhi ) ); + sb.append( mDecimalFormatter.format( (double)rxfreqhi / 1E6d ) ); } else { @@ -184,10 +184,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN TX LOW LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double txfreq = getFrequency(); - if( txfreq != sDOUBLE_NULL_VALUE ) + long txfreq = getFrequency(); + if( txfreq > 0 ) { - sb.append( mDecimalFormatter.format( txfreq ) ); + sb.append( mDecimalFormatter.format( (double)txfreq / 1E6d ) ); } else { @@ -199,10 +199,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN TX HIGH LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double txFreqLo = getFrequency(); - if( txFreqLo != sDOUBLE_NULL_VALUE ) + long txFreqLo = getFrequency(); + if( txFreqLo > 0 ) { - sb.append( mDecimalFormatter.format( txFreqLo ) ); + sb.append( mDecimalFormatter.format( (double)txFreqLo / 1E6d ) ); } else { @@ -224,7 +224,7 @@ public class LTRNetOSWMessage extends LTRNetMessage int neighborRank = getNeighborRank(); String neighborId = getNeighborID(); - if( neighborRank != sINT_NULL_VALUE && + if( neighborRank != INT_NULL_VALUE && neighborId != null ) { sb.append( "NEIGHBOR SITE " ); @@ -335,7 +335,7 @@ public class LTRNetOSWMessage extends LTRNetMessage public int getRadioUniqueID() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.ID_UNIQ ) { @@ -379,7 +379,12 @@ public class LTRNetOSWMessage extends LTRNetMessage { if( mMessageType == MessageType.ID_NBOR ) { - return String.valueOf( mMessage.getInt( 23, 32 ) ); + int id = mMessage.getInt( 23, 32 ); + + if( id > 0 ) + { + return String.valueOf( id ); + } } return null; @@ -397,7 +402,7 @@ public class LTRNetOSWMessage extends LTRNetMessage public int getNeighborRank() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.ID_NBOR ) { @@ -407,9 +412,9 @@ public class LTRNetOSWMessage extends LTRNetMessage return retVal; } - public double getFrequency() + public long getFrequency() { - double retVal = sDOUBLE_NULL_VALUE; + long retVal = 0; if( mAuxMessage != null ) { @@ -429,9 +434,9 @@ public class LTRNetOSWMessage extends LTRNetMessage low = getLowChannelUnits(); } - if( high != sINT_NULL_VALUE && low != sINT_NULL_VALUE ) + if( high != INT_NULL_VALUE && low != INT_NULL_VALUE ) { - retVal = ( high + low ) * .00125 + 150.0; + retVal = ( high + low ) * 1250 + 150000000; } } @@ -445,7 +450,7 @@ public class LTRNetOSWMessage extends LTRNetMessage */ public int getHighChannelUnits() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mAuxMessage != null && ( mMessageType == MessageType.FQ_RXHI || @@ -466,7 +471,7 @@ public class LTRNetOSWMessage extends LTRNetMessage */ public int getLowChannelUnits() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.FQ_RXLO || mMessageType == MessageType.FQ_TXLO ) @@ -490,20 +495,20 @@ public class LTRNetOSWMessage extends LTRNetMessage { switch( channel ) { - case sCHIU_UNIQUE_ID: + case CHIU_UNIQUE_ID: retVal = MessageType.ID_UNIQ; break; - case sCHIU_SITE_ID: + case CHIU_SITE_ID: retVal = MessageType.ID_SITE; break; - case sCHIU_TRANSMIT_FREQUENCY: + case CHIU_TRANSMIT_FREQUENCY: if( getHomeRepeater() == 24 ) { retVal = MessageType.ID_UNIQ; } else { - if( mMessage.get( sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) + if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) { retVal = MessageType.FQ_TXHI; } @@ -513,8 +518,8 @@ public class LTRNetOSWMessage extends LTRNetMessage } } break; - case sCHIU_RECEIVE_FREQUENCY: - if( mMessage.get( sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) + case CHIU_RECEIVE_FREQUENCY: + if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) { retVal = MessageType.FQ_RXHI; } @@ -523,11 +528,11 @@ public class LTRNetOSWMessage extends LTRNetMessage retVal = MessageType.FQ_RXLO; } break; - case sCHIU_NEIGHBOR: + case CHIU_NEIGHBOR: retVal = MessageType.ID_NBOR; break; - case sCHIU_MAP: - if( mMessage.get( sCHANNEL_MAP_MESSAGE_TYPE_BIT ) ) + case CHIU_MAP: + if( mMessage.get( CHANNEL_MAP_MESSAGE_TYPE_BIT ) ) { retVal = MessageType.MA_CHNH; } @@ -536,7 +541,7 @@ public class LTRNetOSWMessage extends LTRNetMessage retVal = MessageType.MA_CHNL; } break; - case sCHIU_CALL_END: + case CHIU_CALL_END: retVal = MessageType.CA_ENDD; } } @@ -544,7 +549,7 @@ public class LTRNetOSWMessage extends LTRNetMessage { int group = getGroup(); - if( group == sGROUP_CHANNEL_IDLE ) + if( group == GROUP_CHANNEL_IDLE ) { retVal = MessageType.SY_IDLE; } diff --git a/src/decode/config/DecodeConfigLTRStandard.java b/src/module/decode/ltrstandard/DecodeConfigLTRStandard.java similarity index 92% rename from src/decode/config/DecodeConfigLTRStandard.java rename to src/module/decode/ltrstandard/DecodeConfigLTRStandard.java index 62118f0f..702ceff1 100644 --- a/src/decode/config/DecodeConfigLTRStandard.java +++ b/src/module/decode/ltrstandard/DecodeConfigLTRStandard.java @@ -15,12 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.ltrstandard; import javax.xml.bind.annotation.XmlAttribute; import message.MessageDirection; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigLTRStandard extends DecodeConfiguration { diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java new file mode 100644 index 00000000..25620a4c --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -0,0 +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 + ******************************************************************************/ +package module.decode.ltrstandard; + +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import message.MessageDirection; +import module.decode.Decoder; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import alias.AliasList; +import bits.MessageFramer; +import bits.SyncPattern; +import dsp.fsk.LTRFSKDecoder; + +public class LTRStandardDecoder extends Decoder implements IRealBufferListener +{ + public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; + + private LTRFSKDecoder mLTRFSKDecoder; + private MessageFramer mLTRMessageFramer; + private LTRStandardMessageProcessor mLTRMessageProcessor; + + public LTRStandardDecoder( AliasList aliasList, + MessageDirection direction ) + { + super( new LTRStandardDecoderState( aliasList ) ); + + mLTRFSKDecoder = new LTRFSKDecoder(); + + if( direction == MessageDirection.OSW ) + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + else + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + + mLTRFSKDecoder.addListener( mLTRMessageFramer ); + + mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); + mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); + mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.LTR_STANDARD; + } + + @Override + public Listener getRealBufferListener() + { + return mLTRFSKDecoder; + } +} diff --git a/src/decode/ltrstandard/LTRPanel.java b/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java similarity index 62% rename from src/decode/ltrstandard/LTRPanel.java rename to src/module/decode/ltrstandard/LTRStandardDecoderPanel.java index 921f9c4a..81a5a4c3 100644 --- a/src/decode/ltrstandard/LTRPanel.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java @@ -15,67 +15,43 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.awt.EventQueue; import javax.swing.JLabel; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; import settings.SettingsManager; import alias.Alias; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; -public class LTRPanel extends ChannelStatePanel +public class LTRStandardDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol = new JLabel( "LTR Standard" ); private JLabel mTalkgroup = new JLabel(); private JLabel mTalkgroupAlias = new JLabel(); private JLabel mLCN = new JLabel( "LCN:" ); - private SettingsManager mSettingsManager; - - public LTRPanel( SettingsManager settingsManager, Channel channel ) + public LTRStandardDecoderPanel( SettingsManager settingsManager, LTRStandardDecoder decoder ) { - super( channel ); + super( settingsManager, decoder ); - channel.getProcessingChain().getChannelState().addListener( this ); - - mSettingsManager = settingsManager; init(); } public void dispose() { super.dispose(); - mSettingsManager = null; } public void init() { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); @@ -88,16 +64,17 @@ public class LTRPanel extends ChannelStatePanel mLCN.setFont( mFontDecoder ); mLCN.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mTalkgroup ); add( mTalkgroupAlias, "wrap" ); add( mLCN, "wrap" ); } + + private LTRStandardDecoderState getDecoderState() + { + return (LTRStandardDecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute attribute ) @@ -107,8 +84,7 @@ public class LTRPanel extends ChannelStatePanel @Override public void run() { - LTRChannelState state = (LTRChannelState)mChannel - .getProcessingChain().getChannelState(); + LTRStandardDecoderState state = getDecoderState(); switch( attribute ) { @@ -118,10 +94,10 @@ public class LTRPanel extends ChannelStatePanel mLCN.setText( "LCN: " + state.getChannelNumber() ); } break; - case FROM_TALKGROUP: + case TO_TALKGROUP: mTalkgroup.setText( state.getTalkgroup() ); /* fall-through */ - case FROM_TALKGROUP_ALIAS: + case TO_TALKGROUP_ALIAS: Alias alias = state.getTalkgroupAlias(); if( alias != null ) @@ -135,18 +111,6 @@ public class LTRPanel extends ChannelStatePanel mTalkgroupAlias.setText( null ); mTalkgroupAlias.setIcon( null ); } - case CHANNEL_STATE: - mStateLabel.setText( state.getState().getDisplayValue() ); - break; - case SOURCE: - mSourceLabel.setText( mChannel - .getSourceConfiguration().getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; default: break; } @@ -168,10 +132,6 @@ public class LTRPanel extends ChannelStatePanel switch( colorSetting.getColorSettingName() ) { case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); @@ -189,15 +149,7 @@ public class LTRPanel extends ChannelStatePanel mLCN.setForeground( mColorLabelDecoder ); } break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } + default: break; } } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java new file mode 100644 index 00000000..bfb26bcb --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -0,0 +1,398 @@ +/******************************************************************************* + * 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 module.decode.ltrstandard; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent.CallEventType; +import module.decode.ltrnet.LTRCallEvent; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class LTRStandardDecoderState extends DecoderState +{ + private static final int sINT_NULL_VALUE = -1; + private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); + + private HashMap mActiveCalls = new HashMap(); + private HashSet mTalkgroupsFirstHeard = new HashSet(); + private TreeSet mTalkgroups = new TreeSet(); + private TreeSet mActiveLCNs = new TreeSet(); + private int mMonitoredLCN; + + private String mTalkgroup; + private Alias mTalkgroupAlias; + private int mChannelNumber; + + private long mFrequency; + + public LTRStandardDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + private LTRCallEvent getCurrentCallEvent() + { + return (LTRCallEvent)mCurrentCallEvent; + } + + @Override + public void receive( Message message ) + { + if( message.isValid() ) + { + if( message instanceof LTRStandardOSWMessage ) + { + LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message); + + switch( ltr.getMessageType() ) + { + case CA_STRT: + if( 0 < ltr.getChannel() && ltr.getChannel() <=20 ) + { + /* If we haven't captured the LCN yet ... */ + if( mChannelNumber == 0 ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + /* If this call event is for this channel, then update + * talkgroup and alias */ + if( ltr.getChannel() == mChannelNumber ) + { + mTalkgroup = ltr.getTalkgroupID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + LTRCallEvent current = getCurrentCallEvent(); + + /* If we have an ongoing call, then add this message + * to the current event - if addMessage returns + * false, then we have a different talkgroup and a + * new call event, so close out the old one. */ + if( current != null && !current.addMessage( ltr ) ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; + } + + if( current == null ) + { + mCurrentCallEvent = + new LTRCallEvent.Builder( CallEventType.CALL ) + .to( ltr.getTalkgroupID() ) + .aliasList( getAliasList() ) + .channel( String.valueOf( ltr.getChannel() ) ) + .frequency( mFrequency ) + .build(); + + broadcast( mCurrentCallEvent ); + + broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) ); + } + else + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) ); + } + } + } + break; + case CA_ENDD: + mTalkgroup = ltr.getTalkgroupID(); + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mTalkgroupAlias = ltr.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + + if( mCurrentCallEvent != null ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + mCurrentCallEvent = null; + } + + int home = ltr.getHomeRepeater(); + + if( 0 < home && home <= 20 ) + { + String talkgroup = ltr.getTalkgroupID( false ); + + if( mTalkgroupsFirstHeard.contains( talkgroup ) ) + { + mTalkgroups.add( talkgroup ); + } + else + { + mTalkgroupsFirstHeard.add( talkgroup ); + } + } + + int channel = ltr.getChannel(); + + if( 0 < channel && channel <= 20 ) + { + if( channel == home ) + { + if( mMonitoredLCN != channel ) + { + mMonitoredLCN = channel; + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + mActiveLCNs.add( channel ); + } + + int free = ltr.getFree(); + + if( 0 < free && free <= 20 ) + { + mActiveLCNs.add( free ); + } + } + + break; + case SY_IDLE: + int idleChannel = ltr.getChannel(); + + if( idleChannel == ltr.getHomeRepeater() && + idleChannel == ltr.getFree() ) + { + if( mMonitoredLCN != idleChannel ) + { + mMonitoredLCN = idleChannel; + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + mActiveLCNs.add( idleChannel ); + } + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.IDLE ) ); + break; + case UN_KNWN: + default: + break; + } + } + } + } + +// /** +// * Intercept the fade event so that we can generate a call end event +// */ +// @Override +// public void fade( final CallEventType type ) +// { +// /* We can receive multiple call tear-down messages -- only respond to +// * the message that can change the state to fade */ +// if( getState().canChangeTo( State.FADE ) ) +// { +// mActiveCalls.clear(); +// +// LTRCallEvent current = getCurrentLTRCallEvent(); +// +// if( current != null ) +// { +// /* Close out the call event. If we only received 1 call +// * message, then flag it as noise and remove the call event */ +// if( current.isValid() ) +// { +// mCallEventModel.setEnd( current ); +// } +// else +// { +// mCallEventModel.remove( current ); +// } +// } +// +// setCurrentCallEvent( null ); +// } +// +// super.fade( type ); +// } + + public void reset() + { + resetState(); + + if( mCurrentCallEvent != null ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; + } + + } + + private void resetState() + { + mTalkgroup = null; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mTalkgroupAlias = null; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getTalkgroup() + { + return mTalkgroup; + } + + public Alias getTalkgroupAlias() + { + return mTalkgroupAlias; + } + + public boolean hasChannelNumber() + { + return mChannelNumber != 0; + } + + public int getChannelNumber() + { + return mChannelNumber; + } + + @Override + public void init() + { + + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = event.getFrequency(); + break; + default: + break; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n\n" ); + + sb.append( "Decoder:\tLTR-Standard\n" ); + + sb.append( "Monitored LCN: " ); + + if( mMonitoredLCN > 0 ) + { + sb.append( mMonitoredLCN ); + } + else + { + sb.append( "unknown" ); + } + + sb.append( "\n" ); + + Integer[] lcns = mActiveLCNs.toArray( new Integer[0] ); + + sb.append( "Active LCNs:\t" ); + + if( lcns.length > 0 ) + { + sb.append( Arrays.toString( lcns ) ); + } + else + { + sb.append( "none" ); + } + sb.append( "\n\n" ); + + sb.append( "Talkgroups\n" ); + + if( mTalkgroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mTalkgroups.iterator(); + + while( it.hasNext() ) + { + String tgid = it.next(); + + sb.append( " " ); + sb.append( formatTalkgroup( tgid ) ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getTalkgroupAlias( tgid ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + 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; + } + } + +} diff --git a/src/decode/ltrstandard/LTRStandardEditor.java b/src/module/decode/ltrstandard/LTRStandardEditor.java similarity index 90% rename from src/decode/ltrstandard/LTRStandardEditor.java rename to src/module/decode/ltrstandard/LTRStandardEditor.java index 57a7de7c..35f0e107 100644 --- a/src/decode/ltrstandard/LTRStandardEditor.java +++ b/src/module/decode/ltrstandard/LTRStandardEditor.java @@ -15,18 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; import message.MessageDirection; -import decode.DecodeEditor; -import decode.config.DecodeConfigLTRStandard; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class LTRStandardEditor extends DecodeEditor { @@ -64,7 +64,8 @@ public class LTRStandardEditor extends DecodeEditor } } } ); - - add( mComboDirection, "span 2" ); + + add( new JLabel( "Format:" ) ); + add( mComboDirection, "wrap" ); } } diff --git a/src/decode/ltrstandard/LTRStandardISWMessage.java b/src/module/decode/ltrstandard/LTRStandardISWMessage.java similarity index 99% rename from src/decode/ltrstandard/LTRStandardISWMessage.java rename to src/module/decode/ltrstandard/LTRStandardISWMessage.java index 44d44385..8f16ac1f 100644 --- a/src/decode/ltrstandard/LTRStandardISWMessage.java +++ b/src/module/decode/ltrstandard/LTRStandardISWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.util.ArrayList; import java.util.Date; diff --git a/src/decode/ltrstandard/LTRStandardMessage.java b/src/module/decode/ltrstandard/LTRStandardMessage.java similarity index 98% rename from src/decode/ltrstandard/LTRStandardMessage.java rename to src/module/decode/ltrstandard/LTRStandardMessage.java index b598d58c..408c7bd9 100644 --- a/src/decode/ltrstandard/LTRStandardMessage.java +++ b/src/module/decode/ltrstandard/LTRStandardMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.text.DecimalFormat; import java.text.SimpleDateFormat; @@ -23,10 +23,10 @@ import java.text.SimpleDateFormat; import message.Message; import message.MessageDirection; import message.MessageType; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.DecoderType; import edac.CRC; import edac.CRCLTR; diff --git a/src/decode/ltrstandard/LTRStandardMessageFilter.java b/src/module/decode/ltrstandard/LTRStandardMessageFilter.java similarity index 97% rename from src/decode/ltrstandard/LTRStandardMessageFilter.java rename to src/module/decode/ltrstandard/LTRStandardMessageFilter.java index 8054fd3a..e51eb243 100644 --- a/src/decode/ltrstandard/LTRStandardMessageFilter.java +++ b/src/module/decode/ltrstandard/LTRStandardMessageFilter.java @@ -1,4 +1,4 @@ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/ltrstandard/LTRStandardMessageProcessor.java b/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java similarity index 67% rename from src/decode/ltrstandard/LTRStandardMessageProcessor.java rename to src/module/decode/ltrstandard/LTRStandardMessageProcessor.java index 510eac6c..ecfb5d12 100644 --- a/src/decode/ltrstandard/LTRStandardMessageProcessor.java +++ b/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,11 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import message.Message; import message.MessageDirection; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -27,8 +26,8 @@ import bits.BinaryMessage; public class LTRStandardMessageProcessor implements Listener { private MessageDirection mDirection; - private Broadcaster mBroadcaster = new Broadcaster(); private AliasList mAliasList; + private Listener mMessageListener; public LTRStandardMessageProcessor( MessageDirection direction, AliasList list ) { @@ -39,29 +38,32 @@ public class LTRStandardMessageProcessor implements Listener @Override public void receive( BinaryMessage buffer ) { - LTRStandardMessage message; - - if( mDirection == MessageDirection.OSW ) + if( mMessageListener != null ) { - message = new LTRStandardOSWMessage( buffer, mAliasList ); - } - else - { - buffer.flip( 0, 40 ); + LTRStandardMessage message; + + if( mDirection == MessageDirection.OSW ) + { + message = new LTRStandardOSWMessage( buffer, mAliasList ); + } + else + { + buffer.flip( 0, 40 ); - message = new LTRStandardISWMessage( buffer, mAliasList ); + message = new LTRStandardISWMessage( buffer, mAliasList ); + } + + mMessageListener.receive( message ); } - - mBroadcaster.receive( message ); } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/ltrstandard/LTRStandardOSWMessage.java b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java similarity index 99% rename from src/decode/ltrstandard/LTRStandardOSWMessage.java rename to src/module/decode/ltrstandard/LTRStandardOSWMessage.java index e29bb9d1..df3bde82 100644 --- a/src/decode/ltrstandard/LTRStandardOSWMessage.java +++ b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.util.ArrayList; import java.util.Date; diff --git a/src/decode/mdc1200/MDCCallEvent.java b/src/module/decode/mdc1200/MDCCallEvent.java similarity index 96% rename from src/decode/mdc1200/MDCCallEvent.java rename to src/module/decode/mdc1200/MDCCallEvent.java index 25f8bb8d..e2fc1dcd 100644 --- a/src/decode/mdc1200/MDCCallEvent.java +++ b/src/module/decode/mdc1200/MDCCallEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class MDCCallEvent extends CallEvent { diff --git a/src/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java similarity index 79% rename from src/decode/mdc1200/MDCDecoder.java rename to src/module/decode/mdc1200/MDCDecoder.java index 79b4d09c..55bf1e68 100644 --- a/src/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -15,28 +15,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import module.decode.Decoder; +import module.decode.DecoderType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.NRZDecoder; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; @@ -44,7 +45,8 @@ import dsp.fsk.FSK2Decoder.Output; * MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * complex or floating point samples and output fully framed MDC1200 messages */ -public class MDCDecoder extends Decoder implements Instrumentable +public class MDCDecoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int sDECIMATED_SAMPLE_RATE = 24000; @@ -57,8 +59,6 @@ public class MDCDecoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER = @@ -67,24 +67,23 @@ public class MDCDecoder extends Decoder implements Instrumentable "Tap Point: NRZ Decoder > < Message Framer"; private FSK2Decoder mFSKDecoder; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private NRZDecoder mNRZDecoder; private MessageFramer mMessageFramer; private MDCMessageProcessor mMessageProcessor; public MDCDecoder( AliasList aliasList ) { - super( SampleType.REAL ); - + super( new MDCDecoderState( aliasList ) ); + /* Decimation filter: 48000 / 2 = 24000 */ - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); /* Bandpass filter - predefined */ - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); mDecimationFilter.setListener( mBandPassFilter ); /* 2FSK Decoder */ @@ -103,8 +102,8 @@ public class MDCDecoder extends Decoder implements Instrumentable /* Message Processor */ mMessageProcessor = new MDCMessageProcessor( aliasList ); - mMessageProcessor.addMessageListener( this ); mMessageFramer.addMessageListener( mMessageProcessor ); + mMessageProcessor.addMessageListener( mMessageBroadcaster ); } public void dispose() @@ -119,12 +118,6 @@ public class MDCDecoder extends Decoder implements Instrumentable mMessageProcessor.dispose(); } - @Override - public DecoderType getType() - { - return DecoderType.MDC1200; - } - /* Instrumentation */ @Override public List getTaps() @@ -133,8 +126,6 @@ public class MDCDecoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( - new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); @@ -154,12 +145,9 @@ public class MDCDecoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - addRealSampleListener( (FloatTap)tap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: /* wire the tap between the bandpass filter and the demod */ - FloatTap floatTap = (FloatTap)tap; + FloatBufferTap floatTap = (FloatBufferTap)tap; mBandPassFilter.setListener( floatTap ); floatTap.setListener( mFSKDecoder ); break; @@ -186,9 +174,6 @@ public class MDCDecoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - removeRealListener( (FloatTap)tap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -203,15 +188,14 @@ public class MDCDecoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } - - @Override - public IAudioOutput getAudioOutput() + public DecoderType getDecoderType() { - // Not implemented - return null; + return DecoderType.MDC1200; + } + + @Override + public Listener getRealBufferListener() + { + return mDecimationFilter; } } diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java new file mode 100644 index 00000000..ea2823f0 --- /dev/null +++ b/src/module/decode/mdc1200/MDCDecoderState.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * 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 module.decode.mdc1200; + +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.State; +import module.decode.state.DecoderStateEvent.Event; +import alias.Alias; +import alias.AliasList; + +public class MDCDecoderState extends DecoderState +{ + private TreeSet mIdents = new TreeSet(); + private TreeSet mEmergencyIdents = new TreeSet(); + + private String mFrom; + private Alias mFromAlias; + private String mTo; + private Alias mToAlias; + private String mMessage; + private String mMessageType; + + public MDCDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void reset() + { + mIdents.clear(); + mEmergencyIdents.clear(); + + resetState(); + } + + private void resetState() + { + setFrom( null ); + setFromAlias( null ); + setTo( null); + setToAlias( null ); + setMessage( null ); + setMessageType( null ); + } + + @Override + public void receive( Message message ) + { + if( message instanceof MDCMessage ) + { + MDCMessage mdc = (MDCMessage)message; + + mIdents.add( mdc.getUnitID() ); + + if( mdc.isEmergency() ) + { + mEmergencyIdents.add( mdc.getUnitID() ); + } + + setFrom( mdc.getFromID() ); + setFromAlias( mdc.getFromIDAlias() ); + + MDCMessageType type = mdc.getMessageType(); + + setMessageType( type.getLabel() ); + + StringBuilder sb = new StringBuilder(); + + sb.append( "OPCODE " ); + sb.append( String.valueOf( mdc.getOpcode() ) ); + + if( mdc.isBOT() ) + { + sb.append( " TYPE:BOT" ); + } + + if( mdc.isEOT() ) + { + sb.append( " TYPE:EOT" ); + } + + setMessage( sb.toString() ); + + MDCCallEvent event = MDCCallEvent.getMDCCallEvent( mdc ); + event.setAliasList( getAliasList() ); + broadcast( event ); + + switch( type ) + { + case ANI: + broadcast( new DecoderStateEvent( this, Event.DECODE, State.CALL ) ); + break; + case ACKNOWLEDGE: + case EMERGENCY: + case PAGING: + case STATUS: + default: + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + break; + } + + } + } + + public String getFrom() + { + return mFrom; + } + + public void setFrom( String from ) + { + mFrom = from; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromAlias() + { + return mFromAlias; + } + + public void setFromAlias( Alias alias ) + { + mFromAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getTo() + { + return mTo; + } + + public void setTo( String to ) + { + mTo = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToAlias() + { + return mToAlias; + } + + public void setToAlias( Alias alias ) + { + mToAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage( String message ) + { + mMessage = message; + broadcast( ChangedAttribute.MESSAGE ); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType( String type ) + { + mMessageType = type; + broadcast( ChangedAttribute.MESSAGE_TYPE ); + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tMDC-1200\n\n" ); + sb.append( "MDC-1200 Idents\n" ); + + if( mIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + 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( "MDC-1200 Emergency Idents\n" ); + + if( mEmergencyIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mEmergencyIdents.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" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization required */ + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + default: + break; + } + } +} diff --git a/src/decode/mdc1200/MDCMessage.java b/src/module/decode/mdc1200/MDCMessage.java similarity index 99% rename from src/decode/mdc1200/MDCMessage.java rename to src/module/decode/mdc1200/MDCMessage.java index 4ba2738b..adb809a3 100644 --- a/src/decode/mdc1200/MDCMessage.java +++ b/src/module/decode/mdc1200/MDCMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/mdc1200/MDCMessageFilter.java b/src/module/decode/mdc1200/MDCMessageFilter.java similarity index 97% rename from src/decode/mdc1200/MDCMessageFilter.java rename to src/module/decode/mdc1200/MDCMessageFilter.java index 729c7756..9da68f8a 100644 --- a/src/decode/mdc1200/MDCMessageFilter.java +++ b/src/module/decode/mdc1200/MDCMessageFilter.java @@ -1,4 +1,4 @@ -package decode.mdc1200; +package module.decode.mdc1200; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/mdc1200/MDCMessageProcessor.java b/src/module/decode/mdc1200/MDCMessageProcessor.java similarity index 99% rename from src/decode/mdc1200/MDCMessageProcessor.java rename to src/module/decode/mdc1200/MDCMessageProcessor.java index a5070666..a1992092 100644 --- a/src/decode/mdc1200/MDCMessageProcessor.java +++ b/src/module/decode/mdc1200/MDCMessageProcessor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import message.Message; import sample.Broadcaster; diff --git a/src/decode/mdc1200/MDCMessageType.java b/src/module/decode/mdc1200/MDCMessageType.java similarity index 97% rename from src/decode/mdc1200/MDCMessageType.java rename to src/module/decode/mdc1200/MDCMessageType.java index 084d34ff..feae9aed 100644 --- a/src/decode/mdc1200/MDCMessageType.java +++ b/src/module/decode/mdc1200/MDCMessageType.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; public enum MDCMessageType { diff --git a/src/decode/mdc1200/MDCPanel.java b/src/module/decode/mdc1200/MDCPanel.java similarity index 75% rename from src/decode/mdc1200/MDCPanel.java rename to src/module/decode/mdc1200/MDCPanel.java index 22226a57..4f87897b 100644 --- a/src/decode/mdc1200/MDCPanel.java +++ b/src/module/decode/mdc1200/MDCPanel.java @@ -15,21 +15,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import java.awt.EventQueue; import javax.swing.JLabel; +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; -public class MDCPanel extends AuxStatePanel +public class MDCPanel extends DecoderPanel { private static final long serialVersionUID = 1L; private static final String sPROTOCOL = "MDC-1200"; @@ -46,35 +47,37 @@ public class MDCPanel extends AuxStatePanel private JLabel mMessage = new JLabel(); private JLabel mMessageType = new JLabel(); - public MDCPanel( SettingsManager settingsManager, MDCChannelState state ) + public MDCPanel( SettingsManager settingsManager, MDCDecoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } - public MDCChannelState getState() + public MDCDecoderState getState() { - return (MDCChannelState)mAuxChannelState; + return (MDCDecoderState)getDecoder().getDecoderState(); } - private void init() + protected void init() { - setOpaque( false ); //Use the parent panel's background color + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); - addLabel( mFromLabel, false ); - addLabel( mFrom, false ); - addLabel( mFromAlias, true ); + add( mFromLabel ); + add( mFrom ); + add( mFromAlias, "wrap" ); - addLabel( mToLabel, false ); - addLabel( mTo, false ); - addLabel( mToAlias, true ); + add( mToLabel ); + add( mTo ); + add( mToAlias, "wrap" ); - addLabel( mProtocol, false ); - addLabel( mMessageType, false ); - addLabel( mMessage, true ); + add( mProtocol ); + add( mMessageType ); + add( mMessage, "wrap" ); } @Override @@ -101,8 +104,16 @@ public class MDCPanel extends AuxStatePanel mFrom.setText( from ); break; case FROM_TALKGROUP_ALIAS: - updateAliasLabel( mFromAlias, - getState().getFromAlias() ); + Alias fromAlias = getState().getFromAlias(); + + if( fromAlias != null ) + { + mFromAlias.setText( fromAlias.getName() ); + } + else + { + mFromAlias.setText( null ); + } break; case TO_TALKGROUP: String to = getState().getTo(); @@ -118,8 +129,16 @@ public class MDCPanel extends AuxStatePanel mTo.setText( to ); break; case TO_TALKGROUP_ALIAS: - updateAliasLabel( mToAlias, - getState().getToAlias() ); + Alias toAlias = getState().getToAlias(); + + if( toAlias != null ) + { + mToAlias.setText( toAlias.getName() ); + } + else + { + mToAlias.setText( null ); + } break; case MESSAGE: mMessage.setText( getState().getMessage() ); @@ -127,6 +146,8 @@ public class MDCPanel extends AuxStatePanel case MESSAGE_TYPE: mMessageType.setText( getState().getMessageType() ); break; + default: + break; } repaint(); diff --git a/src/module/decode/mpt1327/DecodeConfigMPT1327.java b/src/module/decode/mpt1327/DecodeConfigMPT1327.java new file mode 100644 index 00000000..926272a3 --- /dev/null +++ b/src/module/decode/mpt1327/DecodeConfigMPT1327.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * 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 module.decode.mpt1327; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import module.decode.mpt1327.MPT1327Decoder.Sync; + +public class DecodeConfigMPT1327 extends DecodeConfiguration +{ + private String mChannelMapName; + private Sync mSync = Sync.NORMAL; + + private int mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT; + + public DecodeConfigMPT1327() + { + super( DecoderType.MPT1327 ); + } + + @XmlElement + public String getChannelMapName() + { + return mChannelMapName; + } + + public void setChannelMapName( String name ) + { + mChannelMapName = name; + } + + @XmlAttribute( name="sync" ) + public Sync getSync() + { + return mSync; + } + + public void setSync( Sync sync ) + { + mSync = sync; + } + + @XmlAttribute( name="call_timeout" ) + public int getCallTimeout() + { + return mCallTimeout; + } + + /** + * Sets the call timeout value in seconds ( 10 - 600 ); + * @param timeout + */ + public void setCallTimeout( int timeout ) + { + if( CALL_TIMEOUT_MINIMUM <= timeout && timeout <= CALL_TIMEOUT_MAXIMUM ) + { + mCallTimeout = timeout; + } + else + { + mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + } + } + + @XmlAttribute( name="traffic_channel_pool_size" ) + public int getTrafficChannelPoolSize() + { + return mTrafficChannelPoolSize; + } + + /** + * Sets the traffic channel pool size which is the maximum number of + * simultaneous traffic channels that can be allocated. + * + * This limits the maximum calls so that busy systems won't cause more + * traffic channels to be allocated than the decoder/software/host computer + * can support. + */ + public void setTrafficChannelPoolSize( int size ) + { + mTrafficChannelPoolSize = size; + } +} diff --git a/src/decode/mpt1327/MPT1327CallEvent.java b/src/module/decode/mpt1327/MPT1327CallEvent.java similarity index 96% rename from src/decode/mpt1327/MPT1327CallEvent.java rename to src/module/decode/mpt1327/MPT1327CallEvent.java index 24ed009b..abeec4be 100644 --- a/src/decode/mpt1327/MPT1327CallEvent.java +++ b/src/module/decode/mpt1327/MPT1327CallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class MPT1327CallEvent extends CallEvent { diff --git a/src/decode/mpt1327/MPT1327Editor.java b/src/module/decode/mpt1327/MPT1327ConfigEditor.java similarity index 50% rename from src/decode/mpt1327/MPT1327Editor.java rename to src/module/decode/mpt1327/MPT1327ConfigEditor.java index e3289311..17c11938 100644 --- a/src/decode/mpt1327/MPT1327Editor.java +++ b/src/module/decode/mpt1327/MPT1327ConfigEditor.java @@ -15,30 +15,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; import java.util.ArrayList; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; -import controller.channel.ChannelMap; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; +import module.decode.mpt1327.MPT1327Decoder.Sync; import controller.channel.ChannelNode; -import decode.DecodeEditor; -import decode.config.DecodeConfigMPT1327; -import decode.config.DecodeConfiguration; -import decode.mpt1327.MPT1327Decoder.Sync; +import controller.channel.map.ChannelMap; -public class MPT1327Editor extends DecodeEditor +public class MPT1327ConfigEditor extends DecodeEditor { private static final long serialVersionUID = 1L; private JComboBox mComboChannelMaps; private JComboBox mComboSync; + private JLabel mCallTimeoutLabel; + private JSlider mCallTimeout; + private JLabel mTrafficChannelPoolSizeLabel; + private JSlider mTrafficChannelPoolSize; private ChannelNode mChannelNode; - public MPT1327Editor( DecodeConfiguration config, ChannelNode channelNode ) + public MPT1327ConfigEditor( DecodeConfiguration config, ChannelNode channelNode ) { super( config ); @@ -47,6 +53,11 @@ public class MPT1327Editor extends DecodeEditor initGUI(); } + private DecodeConfigMPT1327 getMPTConfig() + { + return (DecodeConfigMPT1327)mConfig; + } + private void initGUI() { /** @@ -61,22 +72,74 @@ public class MPT1327Editor extends DecodeEditor mComboChannelMaps.setModel( new DefaultComboBoxModel( mapArray ) ); - add( new JLabel( "Channel Map" ), "wrap" ); + add( new JLabel( "Channel Map:" ) ); add( mComboChannelMaps, "wrap" ); mComboSync = new JComboBox(); mComboSync.setModel( new DefaultComboBoxModel( Sync.values() ) ); - add( new JLabel( "Sync" ), "wrap" ); + add( new JLabel( "Sync:" ) ); add( mComboSync, "wrap" ); + mCallTimeout = new JSlider( JSlider.HORIZONTAL, + DecodeConfiguration.CALL_TIMEOUT_MINIMUM, + DecodeConfiguration.CALL_TIMEOUT_MAXIMUM, + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); + + mCallTimeout.setMajorTickSpacing( 90 ); + mCallTimeout.setMinorTickSpacing( 10 ); + mCallTimeout.setPaintTicks( true ); + + mCallTimeout.setLabelTable( mCallTimeout.createStandardLabels( 100, 100 ) ); + mCallTimeout.setPaintLabels( true ); + + mCallTimeoutLabel = new JLabel( "Call Timeout: " + mCallTimeout.getValue() + " " ); + + mCallTimeout.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mCallTimeoutLabel.setText( "Call Timeout: " + mCallTimeout.getValue() ); + } + } ); + + add( mCallTimeoutLabel ); + add( mCallTimeout, "wrap,grow" ); + + mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + + mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); + mTrafficChannelPoolSize.setMinorTickSpacing( 1 ); + mTrafficChannelPoolSize.setPaintTicks( true ); + + mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) ); + mTrafficChannelPoolSize.setPaintLabels( true ); + + mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool Size: " + mTrafficChannelPoolSize.getValue() + " " ); + + mTrafficChannelPoolSize.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool Size: " + mTrafficChannelPoolSize.getValue() ); + } + } ); + + add( mTrafficChannelPoolSizeLabel ); + add( mTrafficChannelPoolSize, "wrap,grow" ); + reset(); } @Override public void save() { - DecodeConfigMPT1327 config = (DecodeConfigMPT1327)mConfig; + DecodeConfigMPT1327 config = getMPTConfig(); ChannelMap selected = (ChannelMap)mComboChannelMaps.getSelectedItem(); @@ -91,6 +154,10 @@ public class MPT1327Editor extends DecodeEditor { config.setSync( selectedSync ); } + + config.setCallTimeout( mCallTimeout.getValue() ); + + config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); } @Override @@ -120,5 +187,9 @@ public class MPT1327Editor extends DecodeEditor mComboChannelMaps.requestFocusInWindow(); mComboSync.setSelectedItem( config.getSync() ); + + mCallTimeout.setValue( config.getCallTimeout() ); + + mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); } } diff --git a/src/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java similarity index 70% rename from src/decode/mpt1327/MPT1327Decoder.java rename to src/module/decode/mpt1327/MPT1327Decoder.java index 7fabfbbe..1ef00a4d 100644 --- a/src/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -15,47 +15,45 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Broadcaster; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter; +import controller.channel.Channel.ChannelType; +import controller.channel.map.ChannelMap; import dsp.filter.FilterFactory; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; import dsp.filter.Window.WindowType; +import dsp.filter.dc.DCRemovalFilterLyons; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; -import dsp.nbfm.NBFMDemodulator; /** * MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * complex or floating point samples and output fully framed MPT1327 control and * traffic messages. */ -public class MPT1327Decoder extends Decoder implements Instrumentable +public class MPT1327Decoder extends Decoder + implements IRealBufferListener, Instrumentable { - /* Determines how quickly the DC remove filter responds */ - private static final double sDC_REMOVAL_RATIO = 0.95; - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int sDECIMATED_SAMPLE_RATE = 24000; @@ -67,7 +65,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable private static final int sMESSAGE_LENGTH = 350; /* Instrumentation Taps */ - private static final String INSTRUMENT_INPUT = "Tap Point: Input"; private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS = "Tap Point: Half-band Decimation Filter > < Low Pass Filter"; private static final String INSTRUMENT_LOW_PASS_TO_DECODER = @@ -76,61 +73,35 @@ public class MPT1327Decoder extends Decoder implements Instrumentable "Tap Point: Decoder > < Sync Detect/Message Framer"; private List mAvailableTaps; - private NBFMDemodulator mNBFMDemodulator; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mLowPassFilter; - private DCRemovalFilter mDCRemovalFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mLowPassFilter; + private DCRemovalFilterLyons mDCRemovalFilter; private FSK2Decoder mFSKDecoder; private Broadcaster mSymbolBroadcaster; private MessageFramer mControlMessageFramer; private MessageFramer mTrafficMessageFramer; private MPT1327MessageProcessor mMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "MPT1327 Decoder Audio Output" ); - public MPT1327Decoder( DecodeConfiguration config, - SampleType sampleType, + public MPT1327Decoder( ChannelMap channelMap, + ChannelType channelType, AliasList aliasList, Sync sync ) { - super( config, sampleType ); - - /* If we're receiving complex samples, do FM demodulation and DC removal - * and feed the output back to this decoder */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - /* I/Q low pass filtering narrow band FM demodulator */ - mNBFMDemodulator = new NBFMDemodulator( - FilterFactory.getLowPass( 48000, 4000, 73, WindowType.HAMMING ), - 1.0002, true ); - - this.addComplexListener( mNBFMDemodulator ); - - /* DC removal filter */ - mDCRemovalFilter = new DCRemovalFilter( sDC_REMOVAL_RATIO ); - - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( mDCRemovalFilter ); - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - } - - /* Demodulated float sample processing */ + super( new MPT1327DecoderState( aliasList, channelMap, channelType ) ); /* Decimation filter - 48000 / 2 = 24000 output */ - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true ); /* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */ - mLowPassFilter = new FloatFIRFilter( + mLowPassFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( 24000, //sampleRate, 2000, //passFrequency, 4000, //stopFrequency, 60, //attenuation, WindowType.HANNING, //windowType, true ) //forceOddLength - , 2.0 ); //Gain + , 2.0f ); //Gain mDecimationFilter.setListener( mLowPassFilter ); /** @@ -168,26 +139,23 @@ public class MPT1327Decoder extends Decoder implements Instrumentable /* Fully decoded and framed messages processor */ mMessageProcessor = new MPT1327MessageProcessor( aliasList ); - mMessageProcessor.addMessageListener( this ); + mMessageProcessor.setMessageListener( mMessageBroadcaster ); mControlMessageFramer.addMessageListener( mMessageProcessor ); mTrafficMessageFramer.addMessageListener( mMessageProcessor ); } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( listener ); - } - } + public Listener getRealBufferListener() + { + return mDecimationFilter; + } @Override - public DecoderType getType() - { - return DecoderType.MPT1327; - } - + public DecoderType getDecoderType() + { + return DecoderType.MPT1327; + } + /** * Cleanup method */ @@ -197,8 +165,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable mSymbolBroadcaster.dispose(); - mAudioOutput.dispose(); - mControlMessageFramer.dispose(); if( mDCRemovalFilter != null ) @@ -206,11 +172,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable mDCRemovalFilter.dispose(); } - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.dispose(); - } - mFSKDecoder.dispose(); mDecimationFilter.dispose(); @@ -230,8 +191,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( - new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f ) ); mAvailableTaps.add( @@ -254,24 +213,13 @@ public class MPT1327Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( inputTap ); - } - else - { - addRealSampleListener( inputTap ); - } - break; case INSTRUMENT_HB1_FILTER_TO_LOW_PASS: - FloatTap hb1Tap = (FloatTap)tap; + FloatBufferTap hb1Tap = (FloatBufferTap)tap; mDecimationFilter.setListener( hb1Tap ); hb1Tap.setListener( mLowPassFilter ); break; case INSTRUMENT_LOW_PASS_TO_DECODER: - FloatTap lowTap = (FloatTap)tap; + FloatBufferTap lowTap = (FloatBufferTap)tap; mLowPassFilter.setListener( lowTap ); lowTap.setListener( mFSKDecoder ); break; @@ -290,17 +238,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.removeListener( inputTap ); - } - else - { - removeRealListener( inputTap ); - } - break; case INSTRUMENT_HB1_FILTER_TO_LOW_PASS: mDecimationFilter.setListener( mLowPassFilter ); break; @@ -355,10 +292,4 @@ public class MPT1327Decoder extends Decoder implements Instrumentable return getLabel(); } } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } } diff --git a/src/module/decode/mpt1327/MPT1327DecoderPanel.java b/src/module/decode/mpt1327/MPT1327DecoderPanel.java new file mode 100644 index 00000000..da4f6a5f --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327DecoderPanel.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * 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 module.decode.mpt1327; + +import java.awt.EventQueue; + +import javax.swing.JLabel; + +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import net.miginfocom.swing.MigLayout; +import settings.ColorSetting; +import settings.Setting; +import settings.SettingsManager; +import alias.Alias; +import controller.channel.Channel.ChannelType; + +public class MPT1327DecoderPanel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + + private JLabel mProtocol = new JLabel( "" ); + private JLabel mSiteOrFromTalkgroup = new JLabel( "" ); + private JLabel mSiteOrFromTalkgroupAlias = new JLabel( "" ); + + private JLabel mToTalkgroupLabel = new JLabel( "" ); + private JLabel mToTalkgroup = new JLabel( "" ); + private JLabel mToTalkgroupAliasLabel = new JLabel( "" ); + + public MPT1327DecoderPanel( SettingsManager settingsManager, MPT1327Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + private MPT1327DecoderState getDecoderState() + { + return (MPT1327DecoderState)getDecoder().getDecoderState(); + } + + public void dispose() + { + super.dispose(); + } + + public void init() + { + super.init(); + + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + mProtocol.setText( "MPT1327" ); + mProtocol.setFont( mFontDecoder ); + mProtocol.setForeground( mColorLabelDecoder ); + + MPT1327DecoderState mptState = getDecoderState(); + + mSiteOrFromTalkgroup.setFont( mFontDecoder ); + mSiteOrFromTalkgroup.setForeground( mColorLabelDecoder ); + + mSiteOrFromTalkgroupAlias.setFont( mFontDecoder ); + mSiteOrFromTalkgroupAlias.setForeground( mColorLabelDecoder ); + + mToTalkgroupLabel.setFont( mFontDetails ); + mToTalkgroupLabel.setForeground( mColorLabelDetails ); + + mToTalkgroup.setFont( mFontDecoder ); + mToTalkgroup.setForeground( mColorLabelDecoder ); + + mToTalkgroupAliasLabel.setFont( mFontDecoder ); + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + + + switch( mptState.getChannelType() ) + { + case STANDARD: + mSiteOrFromTalkgroup.setText( mptState.getSite() ); + setAliasLabel( mSiteOrFromTalkgroupAlias, mptState.getSiteAlias() ); + break; + case TRAFFIC: + mSiteOrFromTalkgroup.setText( mptState.getFromTalkgroup() ); + setAliasLabel( mSiteOrFromTalkgroupAlias, mptState.getFromTalkgroupAlias() ); + + mToTalkgroupLabel.setText( "To:" ); + mToTalkgroup.setText( getDecoderState().getToTalkgroup() ); + setAliasLabel( mToTalkgroupAliasLabel, mptState.getToTalkgroupAlias() ); + break; + default: + throw new IllegalArgumentException( "Unrecognized channel " + + "type in MPT1327 Decoder [" + getDecoderState() + .getChannelType().name() + "]" ); + } + + add( mProtocol ); + add( mSiteOrFromTalkgroup ); + add( mSiteOrFromTalkgroupAlias, "wrap" ); + + if( mptState.getChannelType() == ChannelType.TRAFFIC ) + { + add( mToTalkgroupLabel ); + add( mToTalkgroup ); + add( mToTalkgroupAliasLabel, "wrap" ); + } + } + + private void setAliasLabel( final JLabel label, final Alias alias ) + { + if( alias != null ) + { + label.setText( alias.getName() ); + + String iconName = alias.getIconName(); + + if( iconName != null ) + { + label.setIcon( mSettingsManager.getImageIcon( iconName, 12 ) ); + } + else + { + label.setIcon( null ); + } + } + else + { + label.setText( "" ); + label.setIcon( null ); + } + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + final MPT1327DecoderState state = getDecoderState(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case CHANNEL_SITE_NUMBER: + mSiteOrFromTalkgroup.setText( String.valueOf( state.getSite() ) ); + setAliasLabel( mSiteOrFromTalkgroupAlias, state.getSiteAlias() ); + repaint(); + break; + case FROM_TALKGROUP: + mSiteOrFromTalkgroup.setText( state.getFromTalkgroup() ); + setAliasLabel( mSiteOrFromTalkgroupAlias, state.getFromTalkgroupAlias() ); + repaint(); + break; + case TO_TALKGROUP: + mToTalkgroup.setText( state.getToTalkgroup() ); + setAliasLabel( mToTalkgroupAliasLabel, state.getToTalkgroupAlias() ); + repaint(); + break; + default: + break; + } + } + } ); + } + + @Override + public void settingChanged( Setting setting ) + { + super.settingChanged( setting ); + + if( setting instanceof ColorSetting ) + { + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) + { + case CHANNEL_STATE_LABEL_DECODER: + if( mProtocol != null ) + { + mProtocol.setForeground( mColorLabelDecoder ); + } + if( mSiteOrFromTalkgroup != null ) + { + mSiteOrFromTalkgroup.setForeground( mColorLabelDecoder ); + } + if( mSiteOrFromTalkgroupAlias != null ) + { + mSiteOrFromTalkgroupAlias.setForeground( mColorLabelDecoder ); + } + if( mToTalkgroup != null ) + { + mToTalkgroup.setForeground( mColorLabelDecoder ); + } + if( mToTalkgroupAliasLabel != null ) + { + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + } + break; + case CHANNEL_STATE_LABEL_DETAILS: + if( mToTalkgroupLabel != null ) + { + mToTalkgroupLabel.setForeground( mColorLabelDetails ); + } + break; + } + } + } +} diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java new file mode 100644 index 00000000..32461577 --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -0,0 +1,572 @@ +/******************************************************************************* + * 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 module.decode.mpt1327; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.mpt1327.MPT1327Message.IdentType; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import module.decode.state.TrafficChannelAllocationEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasList; +import controller.channel.Channel.ChannelType; +import controller.channel.map.ChannelMap; + +public class MPT1327DecoderState extends DecoderState +{ + private final static Logger mLog = LoggerFactory.getLogger( MPT1327DecoderState.class ); + + private TreeSet mIdents = new TreeSet(); + private HashMap> mGroups = new HashMap>(); + + private String mSite; + private String mFromTalkgroup; + private String mToTalkgroup; + + private int mChannelNumber; + private ChannelType mChannelType; + private ChannelMap mChannelMap; + + private long mFrequency = 0; + + public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap, + ChannelType channelType ) + { + super( aliasList ); + + mChannelMap = channelMap; + + mChannelType = channelType; + } + + public ChannelType getChannelType() + { + return mChannelType; + } + + public void dispose() + { + super.dispose(); + } + + @Override + public void receive( Message message ) + { + if( message.isValid() ) + { + if( message instanceof MPT1327Message ) + { + MPT1327Message mpt = ((MPT1327Message)message); + + switch( mpt.getMessageType() ) + { + case ACK: + mIdents.add( mpt.getFromID() ); + + IdentType identType = mpt.getIdent1Type(); + + if( identType == IdentType.REGI ) + { + broadcast( new MPT1327CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( "REGISTERED ON NETWORK" ) + .frequency( mFrequency ) + .from( mpt.getToID() ) + .to( mpt.getFromID() ) + .build() ); + } + else + { + broadcast( new MPT1327CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( "ACK " + identType.getLabel() ) + .frequency( mFrequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + } + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case ACKI: + mIdents.add( mpt.getFromID() ); + mIdents.add( mpt.getToID() ); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case AHYC: + mIdents.add( mpt.getToID() ); + + broadcast( new MPT1327CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( ( (MPT1327Message) message ).getRequestString() ) + .frequency( mFrequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case AHYQ: + broadcast( new MPT1327CallEvent.Builder( CallEventType.STATUS ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( mpt.getStatusMessage() ) + .frequency( mFrequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case ALH: + String site = mpt.getSiteID(); + + if( site != null && ( mSite == null || !site.contentEquals( mSite ) ) ) + { + mSite = site; + broadcast( ChangedAttribute.CHANNEL_SITE_NUMBER ); + } + mSite = mpt.getSiteID(); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case GTC: + String from = mpt.getFromID(); + String to = mpt.getToID(); + + if( from != null ) + { + mIdents.add( from ); + } + + if( to != null ) + { + mIdents.add( to ); + } + + /* 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( 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( CallEventType.SDM ) + .aliasList( getAliasList() ) + .details( mpt.getMessage() ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + + /* Traffic Channel Events */ + case CLEAR: + case MAINT: + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + break; + } + } + } + } + +// /** +// * Intercept the fade event so that we can generate a call end event +// */ +// @Override +// public void fade( final CallEventType type ) +// { +// /* +// * We can receive multiple call tear-down messages -- only post a call +// * end event for the one that can change the state to fade +// */ +// if( getState().canChangeTo( State.FADE ) ) +// { +// CallEvent current = getCurrentCallEvent(); +// +// if( current != null ) +// { +// mCallEventModel.setEnd( current ); +// } +// else +// { +// mCallEventModel.add( +// new MPT1327CallEvent.Builder( type ) +// .aliasList( getAliasList() ) +// .channel( String.valueOf( mChannelNumber ) ) +// .frequency( mChannelMap.getFrequency( mChannelNumber ) ) +// .from( mFromTalkgroup ) +// .to( mToTalkgroup ) +// .build() ); +// } +// +// setCurrentCall( null ); +// } +// +// super.fade( type ); +// } + + public void reset() + { + mIdents.clear(); + + resetState(); + } + + private void resetState() + { + mFromTalkgroup = null; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mToTalkgroup = null; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public String getSite() + { + return mSite; + } + + public Alias getSiteAlias() + { + if( hasAliasList() ) + { + return getAliasList().getSiteID( mSite ); + } + + return null; + } + + public ChannelMap getChannelMap() + { + return mChannelMap; + } + + public void setChannelMap( ChannelMap channelMap ) + { + mChannelMap = channelMap; + } + + public String getFromTalkgroup() + { + return mFromTalkgroup; + } + + /** + * Set the talkgroup. This is used primarily for traffic channels since + * the talkgroup will already have been identified prior to the traffic + * channel being created. + */ + public void setFromTalkgroup( String fromTalkgroup ) + { + mFromTalkgroup = fromTalkgroup; + + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromTalkgroupAlias() + { + if( hasAliasList() ) + { + return getAliasList().getTalkgroupAlias( mFromTalkgroup ); + } + + return null; + } + + public String getToTalkgroup() + { + return mToTalkgroup; + } + + /** + * Set the talkgroup. This is used primarily for traffic channels since + * the talkgroup will already have been identified prior to the traffic + * channel being created. + */ + public void setToTalkgroup( String toTalkgroup ) + { + mToTalkgroup = toTalkgroup; + + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToTalkgroupAlias() + { + if( hasAliasList() ) + { + return getAliasList().getTalkgroupAlias( mToTalkgroup ); + } + + return null; + } + + + public int getChannelNumber() + { + return mChannelNumber; + } + /** + * Set the channel number. This is used primarily for traffic channels since + * the channel will already have been identified prior to the traffic + * channel being created. + */ + public void setChannelNumber( int channel ) + { + mChannelNumber = channel; + + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tMPT-1327\n" ); + sb.append( "Site:\t" ); + + if( mSite != null ) + { + sb.append( mSite ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( mSite ); + + if( siteAlias != null ) + { + sb.append( " " ); + sb.append( siteAlias.getName() ); + sb.append( "\n" ); + } + } + } + else + { + sb.append( "Unknown\n" ); + } + + sb.append( "\n\n" ); + + sb.append( "Talkgroups\n\n" ); + + if( mGroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + List talkgroups = new ArrayList( mGroups.keySet() ); + Collections.sort( talkgroups ); + + for( String talkgroup: talkgroups ) + { + sb.append( " " ); + sb.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 ); + Collections.sort( members ); + + for( String member: members ) + { + sb.append( " >" ); + sb.append( member ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMPT1327Alias( member ); + + if( alias != null ) + { + sb.append( " " ); + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + + sb.append( "\n" ); + } + } + + sb.append( "\n" ); + + sb.append( "Individual Idents\n" ); + if( mIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mIdents.iterator(); + + while( it.hasNext() ) + { + 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" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization required */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = 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 + { + mChannelNumber = Integer.valueOf( channel ); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + catch( Exception e ) + { + //Do nothing, we couldn't parse the channel number + } + } + + mFrequency = allocationEvent.getCallEvent().getFrequency(); + broadcast( ChangedAttribute.SOURCE ); + + mFromTalkgroup = allocationEvent.getCallEvent().getFromID(); + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mToTalkgroup = allocationEvent.getCallEvent().getToID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + } + break; + default: + break; + } + } +} diff --git a/src/decode/mpt1327/MPT1327Message.java b/src/module/decode/mpt1327/MPT1327Message.java similarity index 99% rename from src/decode/mpt1327/MPT1327Message.java rename to src/module/decode/mpt1327/MPT1327Message.java index 6038cef5..206fafa9 100644 --- a/src/decode/mpt1327/MPT1327Message.java +++ b/src/module/decode/mpt1327/MPT1327Message.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; import java.text.DecimalFormat; import java.text.SimpleDateFormat; diff --git a/src/decode/mpt1327/MPT1327MessageFilter.java b/src/module/decode/mpt1327/MPT1327MessageFilter.java similarity index 93% rename from src/decode/mpt1327/MPT1327MessageFilter.java rename to src/module/decode/mpt1327/MPT1327MessageFilter.java index e98f89b9..6ad9be50 100644 --- a/src/decode/mpt1327/MPT1327MessageFilter.java +++ b/src/module/decode/mpt1327/MPT1327MessageFilter.java @@ -1,11 +1,11 @@ -package decode.mpt1327; +package module.decode.mpt1327; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import message.Message; -import decode.mpt1327.MPT1327Message.MPTMessageType; +import module.decode.mpt1327.MPT1327Message.MPTMessageType; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/mpt1327/MPT1327MessageProcessor.java b/src/module/decode/mpt1327/MPT1327MessageProcessor.java similarity index 66% rename from src/decode/mpt1327/MPT1327MessageProcessor.java rename to src/module/decode/mpt1327/MPT1327MessageProcessor.java index 1c73216a..1d1d01e2 100644 --- a/src/decode/mpt1327/MPT1327MessageProcessor.java +++ b/src/module/decode/mpt1327/MPT1327MessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,17 +15,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import message.Message; -import sample.Broadcaster; +import module.decode.DecoderFactory; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; public class MPT1327MessageProcessor implements Listener { - private Broadcaster mBroadcaster = new Broadcaster(); + private final static Logger mLog = LoggerFactory.getLogger( MPT1327MessageProcessor.class ); + + private Listener mMessageListener; private AliasList mAliasList; @@ -36,24 +41,27 @@ public class MPT1327MessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; } @Override public void receive( BinaryMessage buffer ) { - MPT1327Message message = new MPT1327Message( buffer, mAliasList ); - - mBroadcaster.receive( message ); + if( mMessageListener != null ) + { + MPT1327Message message = new MPT1327Message( buffer, mAliasList ); + + mMessageListener.receive( message ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/module/decode/mpt1327/MPT1327TrafficPanel.java b/src/module/decode/mpt1327/MPT1327TrafficPanel.java new file mode 100644 index 00000000..f52d47fc --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327TrafficPanel.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * 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 module.decode.mpt1327; + +import java.awt.EventQueue; + +import javax.swing.JLabel; + +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import settings.ColorSetting; +import settings.Setting; +import settings.SettingsManager; +import alias.Alias; + +public class MPT1327TrafficPanel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + + private JLabel mProtocolLabel; + private JLabel mFromTalkgroupLabel; + private JLabel mFromTalkgroupAliasLabel; + + private JLabel mToLabel; + private JLabel mToTalkgroupLabel; + private JLabel mToTalkgroupAliasLabel; + + public MPT1327TrafficPanel( SettingsManager settingsManager, MPT1327Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + private MPT1327DecoderState getDecoderState() + { + return (MPT1327DecoderState)getDecoder().getDecoderState(); + } + + public void dispose() + { + super.dispose(); + } + + public void init() + { + mProtocolLabel = new JLabel( "MPT1327" ); + mProtocolLabel.setFont( mFontDecoder ); + mProtocolLabel.setForeground( mColorLabelDecoder ); + + mFromTalkgroupLabel = new JLabel(); + mFromTalkgroupLabel.setFont( mFontDecoder ); + mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); + + mFromTalkgroupAliasLabel = new JLabel(); + mFromTalkgroupAliasLabel.setFont( mFontDecoder ); + mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + + mToLabel = new JLabel( "TO:" ); + mToLabel.setFont( mFontDetails ); + mToLabel.setForeground( mColorLabelDetails ); + + mToTalkgroupLabel = new JLabel( getDecoderState().getFromTalkgroup() ); + mToTalkgroupLabel.setFont( mFontDecoder ); + mToTalkgroupLabel.setForeground( mColorLabelDecoder ); + + mToTalkgroupAliasLabel = new JLabel(); + mToTalkgroupAliasLabel.setFont( mFontDecoder ); + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + + add( mProtocolLabel ); + add( mFromTalkgroupLabel ); + add( mFromTalkgroupAliasLabel, "wrap" ); + + add( mToLabel ); + add( mToTalkgroupLabel ); + add( mToTalkgroupAliasLabel, "wrap" ); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + final MPT1327DecoderState state = getDecoderState(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case FROM_TALKGROUP: + String talkgroup = state.getFromTalkgroup(); + + mFromTalkgroupLabel.setText( talkgroup ); + + Alias alias = state.getFromTalkgroupAlias(); + + if( alias != null ) + { + mFromTalkgroupAliasLabel.setText( alias.getName() ); + + String icon = alias.getIconName(); + + mFromTalkgroupAliasLabel.setIcon( mSettingsManager + .getImageIcon( icon, 12 ) ); + } + break; + case TO_TALKGROUP: + String toTalkgroup = state.getToTalkgroup(); + + mToTalkgroupLabel.setText( toTalkgroup ); + + Alias toAlias = state.getToTalkgroupAlias(); + + if( toAlias != null ) + { + mToTalkgroupAliasLabel.setText( toAlias.getName() ); + + String icon = toAlias.getIconName(); + + if( icon != null ) + { + mToTalkgroupAliasLabel.setIcon( mSettingsManager + .getImageIcon( icon, 12 ) ); + } + } + break; + default: + break; + } + + repaint(); + } + } ); + } + + @Override + public void settingChanged( Setting setting ) + { + super.settingChanged( setting ); + + if( setting instanceof ColorSetting ) + { + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) + { + case CHANNEL_STATE_LABEL_DECODER: + mProtocolLabel.setForeground( mColorLabelDecoder ); + mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); + mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + mToTalkgroupLabel.setForeground( mColorLabelDecoder ); + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + break; + case CHANNEL_STATE_LABEL_DETAILS: + mToLabel.setForeground( mColorLabelDetails ); + break; + } + } + } +} diff --git a/src/decode/config/DecodeConfigNBFM.java b/src/module/decode/nbfm/DecodeConfigNBFM.java similarity index 90% rename from src/decode/config/DecodeConfigNBFM.java rename to src/module/decode/nbfm/DecodeConfigNBFM.java index 625ffffc..8d1b5b90 100644 --- a/src/decode/config/DecodeConfigNBFM.java +++ b/src/module/decode/nbfm/DecodeConfigNBFM.java @@ -15,9 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.nbfm; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigNBFM extends DecodeConfiguration { diff --git a/src/module/decode/nbfm/NBFMDecoder.java b/src/module/decode/nbfm/NBFMDecoder.java new file mode 100644 index 00000000..6a90fb80 --- /dev/null +++ b/src/module/decode/nbfm/NBFMDecoder.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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 module.decode.nbfm; + +import module.decode.DecoderType; +import module.decode.PrimaryDecoder; +import module.decode.config.DecodeConfiguration; +import module.decode.state.AlwaysUnsquelchedDecoderState; + +public class NBFMDecoder extends PrimaryDecoder +{ + public NBFMDecoder( DecodeConfiguration config ) + { + super( config, new AlwaysUnsquelchedDecoderState( "NBFM" ) ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.NBFM; + } +} diff --git a/src/decode/lj1200/LJ1200Panel.java b/src/module/decode/nbfm/NBFMDecoderPanel.java similarity index 62% rename from src/decode/lj1200/LJ1200Panel.java rename to src/module/decode/nbfm/NBFMDecoderPanel.java index d63d49d3..ab39d039 100644 --- a/src/decode/lj1200/LJ1200Panel.java +++ b/src/module/decode/nbfm/NBFMDecoderPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,55 +15,49 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.nbfm; import javax.swing.JLabel; import net.miginfocom.swing.MigLayout; +import module.decode.Decoder; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; +import settings.ColorSetting.ColorSettingName; -public class LJ1200Panel extends AuxStatePanel +public class NBFMDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private static final String PROTOCOL = "LJ-1200"; - private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mProtocol = new JLabel( "NBFM" ); - public LJ1200Panel( SettingsManager settingsManager, LJ1200ChannelState state ) + public NBFMDecoderPanel( SettingsManager settingsManager, Decoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } - public void dispose() + protected void init() { - super.dispose(); - } - - public LJ1200ChannelState getState() - { - return (LJ1200ChannelState)mAuxChannelState; - } - - private void init() - { - setOpaque( false ); //Use the parent panel's background color - - setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); + super.init(); - addLabel( mProtocol, false ); + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + mProtocol.setFont( mFontDecoder ); + mProtocol.setForeground( mColorLabelDecoder ); + + add( mProtocol, "wrap" ); } @Override - public void receive( final ChangedAttribute changedAttribute ) - { - } + public void receive( ChangedAttribute t ) + { + /* Not implemented */ + } @Override public void settingChanged( Setting setting ) @@ -75,7 +69,7 @@ public class LJ1200Panel extends AuxStatePanel ColorSetting color = (ColorSetting)setting; if( color.getColorSettingName() == - ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ) { mProtocol.setForeground( color.getColor() ); } diff --git a/src/decode/nbfm/NBFMEditor.java b/src/module/decode/nbfm/NBFMEditor.java similarity index 91% rename from src/decode/nbfm/NBFMEditor.java rename to src/module/decode/nbfm/NBFMEditor.java index e5be3f75..d80e099a 100644 --- a/src/decode/nbfm/NBFMEditor.java +++ b/src/module/decode/nbfm/NBFMEditor.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.nbfm; +package module.decode.nbfm; import javax.swing.JCheckBox; -import decode.DecodeEditor; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class NBFMEditor extends DecodeEditor { diff --git a/src/decode/p25/C4FMFrameListener.java b/src/module/decode/p25/C4FMFrameListener.java similarity index 92% rename from src/decode/p25/C4FMFrameListener.java rename to src/module/decode/p25/C4FMFrameListener.java index ce4767c1..befb0468 100644 --- a/src/decode/p25/C4FMFrameListener.java +++ b/src/module/decode/p25/C4FMFrameListener.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import bits.BinaryMessage; diff --git a/src/decode/p25/C4FMSlicer.java b/src/module/decode/p25/C4FMSlicer.java similarity index 98% rename from src/decode/p25/C4FMSlicer.java rename to src/module/decode/p25/C4FMSlicer.java index 720c290e..6a7d92e3 100644 --- a/src/decode/p25/C4FMSlicer.java +++ b/src/module/decode/p25/C4FMSlicer.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import dsp.symbol.Dibit; import sample.Broadcaster; diff --git a/src/decode/p25/C4FMSymbolFilter.java b/src/module/decode/p25/C4FMSymbolFilter.java similarity index 99% rename from src/decode/p25/C4FMSymbolFilter.java rename to src/module/decode/p25/C4FMSymbolFilter.java index bafff11f..339be4ff 100644 --- a/src/decode/p25/C4FMSymbolFilter.java +++ b/src/module/decode/p25/C4FMSymbolFilter.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import instrument.Instrumentable; import instrument.tap.Tap; @@ -232,6 +232,7 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, @Override public void receive( float sample ) { + //TODO: limit frequency corrections to once per buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11 if( mReset ) { mGainController.reset(); diff --git a/src/module/decode/p25/DecodeConfigP25Phase1.java b/src/module/decode/p25/DecodeConfigP25Phase1.java new file mode 100644 index 00000000..aa19583f --- /dev/null +++ b/src/module/decode/p25/DecodeConfigP25Phase1.java @@ -0,0 +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 + ******************************************************************************/ +package module.decode.p25; + +import javax.xml.bind.annotation.XmlAttribute; + +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import module.decode.p25.P25Decoder.Modulation; + +public class DecodeConfigP25Phase1 extends DecodeConfiguration +{ + private P25_LSMDecoder.Modulation mModulation = Modulation.C4FM; + + private int mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT; + + public DecodeConfigP25Phase1() + { + super( DecoderType.P25_PHASE1 ); + + setAFC( false ); + } + + public boolean supportsAFC() + { + return false; + } + + @XmlAttribute( name = "modulation" ) + public P25_LSMDecoder.Modulation getModulation() + { + return mModulation; + } + + public void setModulation( P25_LSMDecoder.Modulation modulation ) + { + mModulation = modulation; + } + + @XmlAttribute( name="call_timeout" ) + public int getCallTimeout() + { + return mCallTimeout; + } + + /** + * Sets the call timeout value in seconds ( 10 - 600 ); + * @param timeout + */ + public void setCallTimeout( int timeout ) + { + if( CALL_TIMEOUT_MINIMUM <= timeout && timeout <= CALL_TIMEOUT_MAXIMUM ) + { + mCallTimeout = timeout; + } + else + { + mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + } + } + + + @XmlAttribute( name="traffic_channel_pool_size" ) + public int getTrafficChannelPoolSize() + { + return mTrafficChannelPoolSize; + } + + /** + * Sets the traffic channel pool size which is the maximum number of + * simultaneous traffic channels that can be allocated. + * + * This limits the maximum calls so that busy systems won't cause more + * traffic channels to be allocated than the decoder/software/host computer + * can support. + */ + public void setTrafficChannelPoolSize( int size ) + { + mTrafficChannelPoolSize = size; + } +} diff --git a/src/decode/p25/P25CallEvent.java b/src/module/decode/p25/P25CallEvent.java similarity index 95% rename from src/decode/p25/P25CallEvent.java rename to src/module/decode/p25/P25CallEvent.java index 1796a88f..3813d46b 100644 --- a/src/decode/p25/P25CallEvent.java +++ b/src/module/decode/p25/P25CallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.p25; +package module.decode.p25; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class P25CallEvent extends CallEvent { @@ -35,7 +35,7 @@ public class P25CallEvent extends CallEvent long frequency, String details ) { - super( DecoderType.MPT1327, callEventType, + super( DecoderType.P25_PHASE1, callEventType, aliasList, fromID, toID, details ); mChannel = channel; diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java new file mode 100644 index 00000000..26ccce3b --- /dev/null +++ b/src/module/decode/p25/P25Decoder.java @@ -0,0 +1,65 @@ +package module.decode.p25; + +import instrument.Instrumentable; +import module.decode.Decoder; +import module.decode.DecoderType; +import source.tuner.frequency.IFrequencyCorrectionController; +import alias.AliasList; +import controller.channel.Channel.ChannelType; + +public abstract class P25Decoder extends Decoder + implements IFrequencyCorrectionController, Instrumentable +{ + private P25MessageProcessor mMessageProcessor; + + public P25Decoder( AliasList aliasList, ChannelType channelType ) + { + super( new P25DecoderState( aliasList, channelType ) ); + + mMessageProcessor = new P25MessageProcessor( aliasList ); + mMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.P25_PHASE1; + } + + protected P25MessageProcessor getMessageProcessor() + { + return mMessageProcessor; + } + + public abstract Modulation getModulation(); + + public enum Modulation + { + CQPSK( "Simulcast (LSM)", "LSM" ), + C4FM( "Normal (C4FM)", "C4FM" ); + + private String mLabel; + private String mShortLabel; + + private Modulation( String label, String shortLabel ) + { + mLabel = label; + mShortLabel = shortLabel; + } + + public String getLabel() + { + return mLabel; + } + + public String getShortLabel() + { + return mShortLabel; + } + + public String toString() + { + return getLabel(); + } + } +} diff --git a/src/decode/p25/P25Panel.java b/src/module/decode/p25/P25DecoderPanel.java similarity index 57% rename from src/decode/p25/P25Panel.java rename to src/module/decode/p25/P25DecoderPanel.java index 6bd67b6e..97701a17 100644 --- a/src/decode/p25/P25Panel.java +++ b/src/module/decode/p25/P25DecoderPanel.java @@ -15,29 +15,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.p25; +package module.decode.p25; import java.awt.EventQueue; import javax.swing.JLabel; +import net.miginfocom.swing.MigLayout; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import module.decode.state.State; import settings.ColorSetting; import settings.Setting; +import settings.SettingsManager; import alias.Alias; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; -import controller.state.ChannelStatePanel; -import decode.config.DecodeConfigP25Phase1; -public class P25Panel extends ChannelStatePanel +public class P25DecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol; private JLabel mFrom = new JLabel( " " ); private JLabel mFromAlias = new JLabel( " " ); @@ -51,35 +47,22 @@ public class P25Panel extends ChannelStatePanel private JLabel mSiteAlias = new JLabel( "" ); - public P25Panel( Channel channel ) + public P25DecoderPanel( SettingsManager settingsManager, P25Decoder decoder ) { - super( channel ); + super( settingsManager, decoder ); - DecodeConfigP25Phase1 p25Config = - (DecodeConfigP25Phase1)channel.getDecodeConfiguration(); - - mProtocol = new JLabel( "P25-1 " + - p25Config.getModulation().getShortLabel() ); + mProtocol = new JLabel( "P25-1 " + decoder.getModulation().getShortLabel() ); init(); } public void init() { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); + super.init(); - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); - mProtocol.setFont( mFontDecoder ); + mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); mFrom.setFont( mFontDecoder ); @@ -106,10 +89,6 @@ public class P25Panel extends ChannelStatePanel mSite.setFont( mFontDetails ); mSite.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mFrom ); add( mFromAlias, "wrap" ); @@ -122,66 +101,47 @@ public class P25Panel extends ChannelStatePanel add( mSite ); add( mSiteAlias, "wrap" ); } + + private P25DecoderState getDecoderState() + { + return (P25DecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute changedAttribute ) { + final P25DecoderState state = getDecoderState(); + EventQueue.invokeLater( new Runnable() { @Override public void run() { - final P25ChannelState channelState = (P25ChannelState)mChannel. - getProcessingChain().getChannelState(); switch( changedAttribute ) { - case CHANNEL_STATE: - State state = channelState.getState(); - - mStateLabel.setText( state.getDisplayValue() ); - - if( state == State.IDLE ) - { - mFrom.setText( null ); - mFromAlias.setText( null ); - mFromAlias.setIcon( null ); - mTo.setText( null ); - mToAlias.setText( null ); - mToAlias.setIcon( null ); - } - break; - case SOURCE: - mSourceLabel.setText( mChannel.getSourceConfiguration() - .getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; case NAC: - mNAC.setText( "NAC:" + channelState.getNAC() ); + mNAC.setText( "NAC:" + state.getNAC() ); break; case SYSTEM: - mSystem.setText( "SYS:" + channelState.getSystem() ); + mSystem.setText( "SYS:" + state.getSystem() ); break; case SITE: - mSite.setText( "SITE:" + channelState.getSite() ); + mSite.setText( "SITE:" + state.getSite() ); break; case SITE_ALIAS: - mSiteAlias.setText( channelState.getSiteAlias() ); + mSiteAlias.setText( state.getSiteAlias() ); break; case FROM_TALKGROUP: - mFrom.setText( channelState.getFromTalkgroup() ); + mFrom.setText( state.getFromTalkgroup() ); break; case FROM_TALKGROUP_ALIAS: - Alias from = channelState.getFromAlias(); + Alias from = state.getFromAlias(); if( from != null ) { mFromAlias.setText( from.getName() ); - mFromAlias.setIcon( getSettingsManager() + mFromAlias.setIcon( mSettingsManager .getImageIcon( from.getIconName(), 12 ) ); } else @@ -191,15 +151,15 @@ public class P25Panel extends ChannelStatePanel } break; case TO_TALKGROUP: - mTo.setText( channelState.getToTalkgroup() ); + mTo.setText( state.getToTalkgroup() ); break; case TO_TALKGROUP_ALIAS: - Alias to = channelState.getToAlias(); + Alias to = state.getToAlias(); if( to != null ) { mToAlias.setText( to.getName() ); - mToAlias.setIcon( getSettingsManager() + mToAlias.setIcon( mSettingsManager .getImageIcon( to.getIconName(), 12 ) ); } else @@ -227,24 +187,12 @@ public class P25Panel extends ChannelStatePanel switch( colorSetting.getColorSettingName() ) { case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); } break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } + default: break; } } diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java new file mode 100644 index 00000000..61de6752 --- /dev/null +++ b/src/module/decode/p25/P25DecoderState.java @@ -0,0 +1,2932 @@ +/******************************************************************************* + * 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 module.decode.p25; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.ldu.LDUMessage; +import module.decode.p25.message.ldu.lc.CallTermination; +import module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser; +import module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; +import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; +import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; +import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; +import module.decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; +import module.decode.p25.message.pdu.confirmed.SNDCPUserData; +import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.CallAlertExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; +import module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; +import module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; +import module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; +import module.decode.p25.message.pdu.osp.control.StatusQueryExtended; +import module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; +import module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; +import module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcast; +import module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdate; +import module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdateExplicit; +import module.decode.p25.message.tdu.lc.NetworkStatusBroadcast; +import module.decode.p25.message.tdu.lc.NetworkStatusBroadcastExplicit; +import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast; +import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; +import module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; +import module.decode.p25.message.tsbk.osp.control.CallAlert; +import module.decode.p25.message.tsbk.osp.control.DenyResponse; +import module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdate; +import module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; +import module.decode.p25.message.tsbk.osp.control.MessageUpdate; +import module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; +import module.decode.p25.message.tsbk.osp.control.QueuedResponse; +import module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; +import module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; +import module.decode.p25.message.tsbk.osp.control.StatusQuery; +import module.decode.p25.message.tsbk.osp.control.StatusUpdate; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; +import module.decode.p25.reference.IPProtocol; +import module.decode.p25.reference.Response; +import module.decode.p25.reference.Service; +import module.decode.p25.reference.Vendor; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import module.decode.state.TrafficChannelAllocationEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasList; +import controller.channel.Channel.ChannelType; + +public class P25DecoderState extends DecoderState +{ + private final static Logger mLog = LoggerFactory.getLogger( P25DecoderState.class ); + private static final DecimalFormat mFrequencyFormatter = + new DecimalFormat( "0.000000" ); + + private module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast mNetworkStatus; + private NetworkStatusBroadcastExtended mNetworkStatusExtended; + private ProtectionParameterBroadcast mProtectionParameterBroadcast; + private RFSSStatusBroadcast mRFSSStatusMessage; + private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; + private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; + private HashMap mNeighborMap = + new HashMap(); + + private Set mSecondaryControlChannels = + new TreeSet<>(); + + private HashMap mBands = + new HashMap(); + + private HashMap mRegistrations = new HashMap(); + private String mLastCommandEventID; + private String mLastPageEventID; + private String mLastQueryEventID; + private String mLastRegistrationEventID; + private String mLastResponseEventID; + + private String mNAC; + private String mSystem; + private String mSite; + private String mSiteAlias; + private String mFromTalkgroup; + private Alias mFromAlias; + private String mToTalkgroup; + private Alias mToAlias; + private String mCurrentChannel = "CURRENT"; + private long mCurrentChannelFrequency = 0; + + private P25CallEvent mCurrentCallEvent; + private ChannelType mChannelType; + + public P25DecoderState( AliasList aliasList, ChannelType channelType ) + { + super( aliasList ); + + mChannelType = channelType; + } + + /** + * Performs a full reset to prepare this object for reuse on a new channel + */ + @Override + public void reset() + { + resetState(); + + updateNAC( null ); + updateSite( null ); + updateSystem( null ); + } + + /** + * Resets any temporal state details + */ + private void resetState() + { + updateFrom( null ); + updateTo( null ); + } + + /** + * Performs any initialization operations to prepare for use + */ + @Override + public void init() + { + } + + /** + * Primary message processing method. + */ + public void receive( Message message ) + { + if( message instanceof P25Message ) + { + updateNAC( ( (P25Message)message ).getNAC() ); + + /* Voice Vocoder messages */ + if( message instanceof LDUMessage ) + { + processLDU( (LDUMessage)message ); + } + + /* Terminator Data Unit with Link Control Message */ + else if( message instanceof TDULinkControlMessage ) + { + processTDULC( (TDULinkControlMessage)message ); + } + + /* Trunking Signalling Messages */ + else if( message instanceof TSBKMessage ) + { + processTSBK( (TSBKMessage)message ); + } + + /* Header Data Unit Message - preceeds voice LDUs */ + else if( message instanceof HDUMessage ) + { + processHDU( (HDUMessage)message ); + } + + /* Packet Data Unit Messages */ + else if( message instanceof PDUMessage ) + { + processPDU( (PDUMessage)message ); + } + + /* Terminator Data Unit, or default message if CRC failed */ + else if( message instanceof TDUMessage ) + { + processTDU( (TDUMessage)message ); + } + } + } + + /** + * Terminator Data Unit, or default message if the CRC failed for a longer + * message. We process these as continuation events. + */ + private void processTDU( TDUMessage tdu ) + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + } + + /** + * Header Data Unit - first message in a call sequence. + */ + private void processHDU( HDUMessage hdu ) + { + if( hdu.isValid() ) + { + State state = State.CALL; + + if( hdu.isEncrypted() ) + { + state = State.ENCRYPTED; + } + + broadcast( new DecoderStateEvent( this, Event.START, state ) ); + + updateTo( hdu.getToID() ); + + if( mChannelType == ChannelType.STANDARD ) + { + StringBuilder sb = new StringBuilder(); + + CallEventType type = CallEventType.CALL; + + if( hdu.isEncrypted() ) + { + type = CallEventType.ENCRYPTED_CALL; + + sb.append( "ENCRYPTED WITH " ); + sb.append( hdu.getEncryption().name() ); + sb.append( " KEY:" ); + sb.append( hdu.getKeyID() ); + } + + P25CallEvent event = new P25CallEvent.Builder( type ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger:HDU" ) + .frequency( mCurrentChannelFrequency ) + .to( hdu.getToID() ) + .details( sb.toString() ) + .build(); + + mCurrentCallEvent = event; + + broadcast( event ); + } + } + } + + /** + * Terminator Data Unit with Link Control - indicates an end of the call + * sequences with embedded link control messages + */ + private void processTDULC( TDULinkControlMessage tdulc ) + { + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + + switch( tdulc.getOpcode() ) + { + case ADJACENT_SITE_STATUS_BROADCAST: + if( tdulc instanceof AdjacentSiteStatusBroadcast ) + { + AdjacentSiteStatusBroadcast assb = + (AdjacentSiteStatusBroadcast)tdulc; + + updateSystem( assb.getSystemID() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + /* This message doesn't provide anything we need for channel state */ + break; + case CALL_ALERT: + if( tdulc instanceof module.decode.p25.message.tdu.lc.CallAlert ) + { + module.decode.p25.message.tdu.lc.CallAlert ca = + (module.decode.p25.message.tdu.lc.CallAlert)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getSourceAddress() ) + .to( ca.getTargetAddress() ) + .details( "CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case CALL_TERMINATION_OR_CANCELLATION: + break; + case CHANNEL_IDENTIFIER_UPDATE: + //TODO: does the activity summary need this message? + + /* This message is handled by the P25MessageProcessor and + * inserted into any channels needing frequency band info */ + break; + case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: + //TODO: does the activity summary need this message? + + /* This message is handled by the P25MessageProcessor and + * inserted into any channels needing frequency band info */ + break; + case EXTENDED_FUNCTION_COMMAND: + if( tdulc instanceof module.decode.p25.message.tdu.lc.ExtendedFunctionCommand ) + { + module.decode.p25.message.tdu.lc.ExtendedFunctionCommand efc = + (module.decode.p25.message.tdu.lc.ExtendedFunctionCommand)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( efc.getTargetAddress() ) + .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + + " ARG:" + efc.getArgument() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case GROUP_AFFILIATION_QUERY: + if( tdulc instanceof module.decode.p25.message.tdu.lc.GroupAffiliationQuery ) + { + module.decode.p25.message.tdu.lc.GroupAffiliationQuery gaq = + (module.decode.p25.message.tdu.lc.GroupAffiliationQuery)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION QUERY" ) + .from( gaq.getSourceAddress() ) + .to( gaq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case GROUP_VOICE_CHANNEL_UPDATE: + /* Reflects other call activity on the system: CALL DETECT */ + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof GroupVoiceChannelUpdate ) + { + GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelA() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyA() ) + .from( gvcu.getGroupAddressA() ) + .to( gvcu.getGroupAddressB() ) + .build() ); + } + 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; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcue.getTransmitChannel() ) + .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcue.getDownlinkFrequency() ) + .to( gvcue.getGroupAddress() ) + .build() ); + } + break; + case GROUP_VOICE_CHANNEL_USER: + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof module.decode.p25.message.tdu.lc.GroupVoiceChannelUser ) + { + module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser = + (module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( gvcuser.getSourceAddress() ) + .to( gvcuser.getGroupAddress() ) + .build() ); + } + break; + case MESSAGE_UPDATE: + if( tdulc instanceof module.decode.p25.message.tdu.lc.MessageUpdate ) + { + module.decode.p25.message.tdu.lc.MessageUpdate mu = + (module.decode.p25.message.tdu.lc.MessageUpdate)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .from( mu.getSourceAddress() ) + .to( mu.getTargetAddress() ) + .details( "MSG: " + mu.getShortDataMessage() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case NETWORK_STATUS_BROADCAST: + if( tdulc instanceof NetworkStatusBroadcast ) + { + updateSystem( ((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 module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast ) + { + module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast ppb = + (module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast)tdulc; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.tdu.lc.RFSSStatusBroadcast ) + { + module.decode.p25.message.tdu.lc.RFSSStatusBroadcast rfsssb = + (module.decode.p25.message.tdu.lc.RFSSStatusBroadcast)tdulc; + + updateSystem( rfsssb.getSystem() ); + + String site = rfsssb.getRFSubsystemID() + "-" + + rfsssb.getSiteID(); + + updateSite( 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 module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit ) + { + module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit rfsssbe = + (module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit)tdulc; + + String site = rfsssbe.getRFSubsystemID() + "-" + + rfsssbe.getSiteID(); + + updateSite( 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 SecondaryControlChannelBroadcast ) + { + SecondaryControlChannelBroadcast sccb = + (SecondaryControlChannelBroadcast)tdulc; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( 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(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case STATUS_QUERY: + if( tdulc instanceof module.decode.p25.message.tdu.lc.StatusQuery ) + { + module.decode.p25.message.tdu.lc.StatusQuery sq = + (module.decode.p25.message.tdu.lc.StatusQuery)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case STATUS_UPDATE: + if( tdulc instanceof module.decode.p25.message.tdu.lc.StatusUpdate ) + { + module.decode.p25.message.tdu.lc.StatusUpdate su = + (module.decode.p25.message.tdu.lc.StatusUpdate)tdulc; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest ) + { + module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest tiar = + (module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest)tdulc; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser ) + { + module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser tivcu = + (module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "TELEPHONE INTERCONNECT " + + ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( null ) + .to( tivcu.getAddress() ) + .build() ); + } + break; + case UNIT_AUTHENTICATION_COMMAND: + if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitAuthenticationCommand ) + { + module.decode.p25.message.tdu.lc.UnitAuthenticationCommand uac = + (module.decode.p25.message.tdu.lc.UnitAuthenticationCommand)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( uac.getCompleteTargetAddress() ) + .details( "AUTHENTICATE" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case UNIT_REGISTRATION_COMMAND: + if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitRegistrationCommand ) + { + module.decode.p25.message.tdu.lc.UnitRegistrationCommand urc = + (module.decode.p25.message.tdu.lc.UnitRegistrationCommand)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( urc.getCompleteTargetAddress() ) + .details( "REGISTER" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest ) + { + module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest uuar = + (module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( uuar.getSourceAddress() ) + .to( uuar.getTargetAddress() ) + .details( "UNIT TO UNIT CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_USER: + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser ) + { + module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser uuvcu = + (module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) + .frequency( mCurrentChannelFrequency ) + .from( uuvcu.getSourceAddress() ) + .to( uuvcu.getTargetAddress() ) + .build() ); + } + break; + default: + break; + } + } + + /** + * Log optional vendor format messages that we don't yet support so that we + * can understand those messages and eventually add support. + */ + private void logAlternateVendorMessage( P25Message p25 ) + { + if( p25 instanceof TDULinkControlMessage ) + { + TDULinkControlMessage tdulc = (TDULinkControlMessage)p25; + + mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() + + "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() + + " " + tdulc.getBinaryMessage() ); + } + else if( p25 instanceof LDU1Message ) + { + LDU1Message ldu1 = (LDU1Message)p25; + + mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() + + "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() + + " " + ldu1.getBinaryMessage() ); + } + else if( p25 instanceof PDUMessage ) + { + PDUMessage pdu = (PDUMessage)p25; + + mLog.debug( "PDU - VENDOR [" + pdu.getVendor() + + "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() + + " " + pdu.getBinaryMessage() ); + } + else + { + mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" ); + } + } + + private void processLDU( LDUMessage ldu ) + { + State state = ( ldu.isValid() && ldu.isEncrypted() ) ? + State.ENCRYPTED : State.CALL; + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) ); + + if( ldu instanceof LDU1Message ) + { + switch( ((LDU1Message)ldu).getOpcode() ) + { + case ADJACENT_SITE_STATUS_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast ) + { + module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast assb = + (module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast)ldu; + + updateSystem( assb.getSystemID() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + + break; + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + /* This message doesn't provide anything we need for channel state */ + break; + case CALL_ALERT: + if( ldu instanceof module.decode.p25.message.ldu.lc.CallAlert ) + { + module.decode.p25.message.ldu.lc.CallAlert ca = + (module.decode.p25.message.ldu.lc.CallAlert)ldu; + broadcast( new P25CallEvent.Builder( 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.END ) ); + 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 module.decode.p25.message.ldu.lc.ExtendedFunctionCommand ) + { + module.decode.p25.message.ldu.lc.ExtendedFunctionCommand efc = + (module.decode.p25.message.ldu.lc.ExtendedFunctionCommand)ldu; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.ldu.lc.GroupAffiliationQuery ) + { + module.decode.p25.message.ldu.lc.GroupAffiliationQuery gaq = + (module.decode.p25.message.ldu.lc.GroupAffiliationQuery)ldu; + + broadcast( new P25CallEvent.Builder( 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( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate ) + { + module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelA() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyA() ) + .to( gvcu.getGroupAddressA() ) + .build() ); + + String userB = gvcu.getGroupAddressB(); + + if( userB != null && !userB.contentEquals( "0000" ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelB() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyB() ) + .to( userB ) + .build() ); + } + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit ) + { + module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcue.getTransmitChannel() ) + .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcue.getDownlinkFrequency() ) + .to( gvcue.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case GROUP_VOICE_CHANNEL_USER: + /* Indicates the current user of the current channel */ + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser ) + { + module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger:LDU-GROUP_VOICE_CHANNEL_USER " + + ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( gvcuser.getSourceAddress() ) + .to( gvcuser.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case MESSAGE_UPDATE: + if( ldu instanceof module.decode.p25.message.ldu.lc.MessageUpdate ) + { + module.decode.p25.message.ldu.lc.MessageUpdate mu = + (module.decode.p25.message.ldu.lc.MessageUpdate)ldu; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.ldu.lc.NetworkStatusBroadcast ) + { + updateSystem( ((module.decode.p25.message.ldu.lc.NetworkStatusBroadcast)ldu).getSystem() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case NETWORK_STATUS_BROADCAST_EXPLICIT: + if( ldu instanceof module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit ) + { + updateSystem( ((module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit)ldu).getSystem() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case PROTECTION_PARAMETER_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast ) + { + module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast ppb = + (module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast)ldu; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.ldu.lc.RFSSStatusBroadcast ) + { + module.decode.p25.message.ldu.lc.RFSSStatusBroadcast rfsssb = + (module.decode.p25.message.ldu.lc.RFSSStatusBroadcast)ldu; + + updateSystem( rfsssb.getSystem() ); + + String site = rfsssb.getRFSubsystemID() + "-" + + rfsssb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case RFSS_STATUS_BROADCAST_EXPLICIT: + if( ldu instanceof module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit ) + { + module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit rfsssbe = + (module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit)ldu; + + String site = rfsssbe.getRFSubsystemID() + "-" + + rfsssbe.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast ) + { + module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast sccb = + (module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast)ldu; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + if( ldu instanceof module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit ) + { + module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit sccb = + (module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit)ldu; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case STATUS_QUERY: + if( ldu instanceof module.decode.p25.message.ldu.lc.StatusQuery ) + { + module.decode.p25.message.ldu.lc.StatusQuery sq = + (module.decode.p25.message.ldu.lc.StatusQuery)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case STATUS_UPDATE: + if( ldu instanceof module.decode.p25.message.ldu.lc.StatusUpdate ) + { + module.decode.p25.message.ldu.lc.StatusUpdate su = + (module.decode.p25.message.ldu.lc.StatusUpdate)ldu; + + broadcast( new P25CallEvent.Builder( 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 module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest ) + { + module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest tiar = + (module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest)ldu; + + broadcast( new P25CallEvent.Builder( 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 TelephoneInterconnectVoiceChannelUser ) + { + TelephoneInterconnectVoiceChannelUser tivcu = + (TelephoneInterconnectVoiceChannelUser)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger: LDU-TEL_INT_VOIC_CHAN_USER " + + "TELEPHONE INTERCONNECT " + + ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( null ) + .to( tivcu.getAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_AUTHENTICATION_COMMAND: + if( ldu instanceof module.decode.p25.message.ldu.lc.UnitAuthenticationCommand ) + { + module.decode.p25.message.ldu.lc.UnitAuthenticationCommand uac = + (module.decode.p25.message.ldu.lc.UnitAuthenticationCommand)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( uac.getCompleteTargetAddress() ) + .details( "AUTHENTICATE" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_REGISTRATION_COMMAND: + if( ldu instanceof module.decode.p25.message.ldu.lc.UnitRegistrationCommand ) + { + module.decode.p25.message.ldu.lc.UnitRegistrationCommand urc = + (module.decode.p25.message.ldu.lc.UnitRegistrationCommand)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( urc.getCompleteTargetAddress() ) + .details( "REGISTER" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + if( ldu instanceof module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest ) + { + module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest uuar = + (module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest)ldu; + + broadcast( new P25CallEvent.Builder( 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 UnitToUnitVoiceChannelUser ) + { + UnitToUnitVoiceChannelUser uuvcu = + (UnitToUnitVoiceChannelUser)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger:LDU-U2U_VOICE_CHAN_USER " + + ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) + .frequency( mCurrentChannelFrequency ) + .from( uuvcu.getSourceAddress() ) + .to( uuvcu.getTargetAddress() ) + .build() ); + } + 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.START, State.CONTROL ) ); + + if( tsbk.getVendor() == Vendor.STANDARD ) + { + switch( tsbk.getOpcode() ) + { + case ADJACENT_STATUS_BROADCAST: + AdjacentStatusBroadcast neighborMessage = + (AdjacentStatusBroadcast)tsbk; + + mNeighborMap.put( neighborMessage.getUniqueID(), neighborMessage ); + 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: + 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 = (module.decode.p25.message.tsbk.osp + .control.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: + module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sccb = + (module.decode.p25.message.tsbk.osp.control.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; + } + } + } + + /** + * Process a Packet Data Unit message + */ + private void processPDU( PDUMessage pdu ) + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) ); + + if( pdu instanceof NetworkStatusBroadcastExtended ) + { + mNetworkStatusExtended = (NetworkStatusBroadcastExtended)pdu; + } + else if( pdu instanceof ProtectionParameterBroadcast ) + { + mProtectionParameterBroadcast = + (ProtectionParameterBroadcast)pdu; + } + else if( pdu instanceof RFSSStatusBroadcastExtended ) + { + mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)pdu; + } + + if( pdu instanceof PDUConfirmedMessage ) + { + PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; + + switch( pduc.getPDUType() ) + { + case SN_ACTIVATE_TDS_CONTEXT_ACCEPT: + SNDCPActivateTDSContextAccept satca = + (SNDCPActivateTDSContextAccept)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "ACTIVATE SNDCP USE IP:" + satca.getIPAddress() ) + .frequency( mCurrentChannelFrequency ) + .to( satca.getLogicalLinkID() ) + .build() ); + break; + case SN_ACTIVATE_TDS_CONTEXT_REJECT: + SNDCPActivateTDSContextReject satcr = + (SNDCPActivateTDSContextReject)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "REJECT: SNDCP CONTEXT ACTIVATION " + + "REASON:" + satcr.getReason().getLabel() ) + .frequency( mCurrentChannelFrequency ) + .to( satcr.getLogicalLinkID() ) + .build() ); + break; + case SN_ACTIVATE_TDS_CONTEXT_REQUEST: + SNDCPActivateTDSContextRequest satcreq = + (SNDCPActivateTDSContextRequest)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "REQUEST SNDCP USE IP:" + satcreq.getIPAddress() ) + .frequency( mCurrentChannelFrequency ) + .from( satcreq.getLogicalLinkID() ) + .build() ); + break; + case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT: + SNDCPDeactivateTDSContext sdtca = + (SNDCPDeactivateTDSContext)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "ACCEPT DEACTIVATE SNDCP CONTEXT" ) + .frequency( mCurrentChannelFrequency ) + .from( sdtca.getLogicalLinkID() ) + .build() ); + break; + case SN_DEACTIVATE_TDS_CONTEXT_REQUEST: + SNDCPDeactivateTDSContext sdtcreq = + (SNDCPDeactivateTDSContext)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "REQUEST DEACTIVATE SNDCP CONTEXT" ) + .frequency( mCurrentChannelFrequency ) + .from( sdtcreq.getLogicalLinkID() ) + .build() ); + break; + case SN_RF_CONFIRMED_DATA: + 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() ); + } + + broadcast( new DecoderStateEvent( this, Event.START, State.DATA ) ); + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "DATA: " + sud.getPayload() + + " RADIO IP:" + sbTo.toString() ) + .frequency( mCurrentChannelFrequency ) + .from( sbFrom.toString() ) + .to( pduc.getLogicalLinkID() ) + .build() ); + break; + default: + mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); + break; + } + } + else + { + switch( pdu.getOpcode() ) + { + case ADJACENT_STATUS_BROADCAST: + AdjacentStatusBroadcastExtended asbe = + (AdjacentStatusBroadcastExtended)pdu; + + updateSystem( asbe.getSystemID() ); + break; + case CALL_ALERT: + if( pdu instanceof CallAlertExtended ) + { + CallAlertExtended ca = (CallAlertExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getWACN() + "-" + ca.getSystemID() + "-" + + ca.getSourceID() ) + .to( ca.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case GROUP_AFFILIATION_QUERY: + if( pdu instanceof GroupAffiliationQueryExtended ) + { + GroupAffiliationQueryExtended gaqe = + (GroupAffiliationQueryExtended)pdu; + + if( mLastQueryEventID == null || !gaqe.getTargetAddress() + .contentEquals( mLastQueryEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION" ) + .from( gaqe.getWACN() + "-" + gaqe.getSystemID() + + "-" + gaqe.getSourceID() ) + .to( gaqe.getTargetAddress() ) + .build() ); + + mLastQueryEventID = gaqe.getToID(); + } + } + break; + case GROUP_AFFILIATION_RESPONSE: + if( pdu instanceof GroupAffiliationResponseExtended ) + { + GroupAffiliationResponseExtended gar = + (GroupAffiliationResponseExtended)pdu; + + if( mLastResponseEventID == null || !gar.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( 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 ); + } + break; + case GROUP_DATA_CHANNEL_GRANT: + if( pdu instanceof GroupDataChannelGrantExtended ) + { + GroupDataChannelGrantExtended gdcge = + (GroupDataChannelGrantExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( gdcge.getTransmitChannel() ) + .details( ( gdcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcge.getDownlinkFrequency() ) + .from( gdcge.getSourceAddress() ) + .to( gdcge.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case GROUP_VOICE_CHANNEL_GRANT: + if( pdu instanceof GroupVoiceChannelGrantExplicit ) + { + GroupVoiceChannelGrantExplicit gvcge = + (GroupVoiceChannelGrantExplicit)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcge.getTransmitChannel() ) + .details( "Trigger: PDU-GRP_V_CHAN_USER" + + ( gvcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcge.getDownlinkFrequency() ) + .from( gvcge.getSourceAddress() ) + .to( gvcge.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case INDIVIDUAL_DATA_CHANNEL_GRANT: + if( pdu instanceof IndividualDataChannelGrantExtended ) + { + IndividualDataChannelGrantExtended idcge = + (IndividualDataChannelGrantExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( idcge.getTransmitChannel() ) + .details( "Trigger:PDU-IND_DATA_CHAN_GRNT " + + ( idcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( idcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( idcge.getDownlinkFrequency() ) + .from( idcge.getSourceWACN() + "-" + + idcge.getSourceSystemID() + "-" + + idcge.getSourceAddress() ) + .to( idcge.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case MESSAGE_UPDATE: + if( pdu instanceof MessageUpdateExtended ) + { + MessageUpdateExtended mu = (MessageUpdateExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .details( "MESSAGE: " + mu.getMessage() ) + .from( mu.getSourceWACN() + "-" + mu.getSourceSystemID() + + "-" + mu.getSourceID() ) + .to( mu.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case RFSS_STATUS_BROADCAST: + if( pdu instanceof RFSSStatusBroadcastExtended ) + { + RFSSStatusBroadcastExtended rsbe = (RFSSStatusBroadcastExtended)pdu; + + updateNAC( rsbe.getNAC() ); + updateSystem( rsbe.getSystemID() ); + updateSite( rsbe.getRFSubsystemID() + "-" + rsbe.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( 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( 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( 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 TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + TelephoneInterconnectChannelGrantExplicit ticge = + (TelephoneInterconnectChannelGrantExplicit)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( ticge.getTransmitChannel() ) + .details( "Trigger: PDU-TEL_INT_V_CH_GRNT " + + "TELEPHONE CALL " + + ( ticge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( ticge.isEmergency() ? " EMERGENCY" : "") + + " CALL TIMER:" + ticge.getCallTimer() ) + .frequency( ticge.getDownlinkFrequency() ) + .from( ticge.getAddress() ) + .build() ); + 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( 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( 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; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + if( pdu instanceof UnitToUnitVoiceChannelGrantExtended ) + { + UnitToUnitVoiceChannelGrantExtended uuvcge = + (UnitToUnitVoiceChannelGrantExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcge.getTransmitChannel() ) + .details( "Trigger:PDU-U2U_V-CH_GRNT " + + ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcge.getDownlinkFrequency() ) + .from( uuvcge.getSourceWACN() + "-" + + uuvcge.getSourceSystemID() + "-" + + uuvcge.getSourceID() ) + .to( uuvcge.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + if( pdu instanceof UnitToUnitVoiceChannelGrantUpdateExtended ) + { + UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = + (UnitToUnitVoiceChannelGrantUpdateExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcgue.getTransmitChannel() ) + .details( "Trigger: PDU-U2U_V_CH_G_U " + + ( uuvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcgue.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgue.getDownlinkFrequency() ) + .from( uuvcgue.getSourceWACN() + "-" + + uuvcgue.getSourceSystemID() + "-" + + uuvcgue.getSourceID() ) + .to( uuvcgue.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + default: + break; + } + } + } + + private void processTSBKCommand( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case AUTHENTICATION_COMMAND: + AuthenticationCommand ac = (AuthenticationCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( ac.getFullTargetID() )) + { + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .details( "AUTHENTICATE" ) + .to( ac.getWACN() + "-" + ac.getSystemID() + "-" + + ac.getTargetID() ) + .build() ); + + mLastCommandEventID = ac.getFullTargetID(); + } + break; + case EXTENDED_FUNCTION_COMMAND: + ExtendedFunctionCommand efc = (ExtendedFunctionCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( efc.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.FUNCTION ) + .aliasList( getAliasList() ) + .details( "EXTENDED FUNCTION: " + + efc.getExtendedFunction().getLabel() ) + .from( efc.getSourceAddress() ) + .to( efc.getTargetAddress() ) + .build() ); + + mLastCommandEventID = efc.getTargetAddress(); + } + break; + case RADIO_UNIT_MONITOR_COMMAND: + RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( rumc.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( 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( 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: + UnitRegistrationCommand urc = (UnitRegistrationCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( urc.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .details( "REGISTER" ) + .from( urc.getSourceAddress() ) + .to( urc.getTargetAddress() ) + .build() ); + + mLastCommandEventID = urc.getTargetAddress(); + } + break; + default: + break; + } + } + + private void processTSBKMessage( TSBKMessage message ) + { + MessageUpdate mu = (MessageUpdate)message; + + broadcast( new P25CallEvent.Builder( 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: + GroupAffiliationQuery gaq = (GroupAffiliationQuery)message; + + if( mLastQueryEventID == null || !mLastQueryEventID + .contentEquals( gaq.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION" ) + .from( gaq.getSourceAddress() ) + .to( gaq.getTargetAddress() ) + .build() ); + } + break; + case STATUS_QUERY: + StatusQuery sq = (StatusQuery)message; + + if( mLastQueryEventID == null || !mLastQueryEventID + .contentEquals( sq.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + } + break; + default: + break; + } + } + + private void processTSBKResponse( TSBKMessage message ) + { + CallEvent event = null; + + 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( 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( 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( 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( 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( 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( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "QUEUED REASON: " + qr.getReason().name() + + " REQUESTED: " + qr.getServiceType().name() ) + .from( qr.getSourceAddress() ) + .to( qr.getTargetAddress() ) + .build() ); + + mLastResponseEventID = qr.getTargetAddress(); + } + break; + case STATUS_UPDATE: + StatusUpdate su = (StatusUpdate)message; + + if( mLastResponseEventID == null || !su.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "STATUS USER: " + su.getUserStatus() + + " UNIT: " + su.getUnitStatus() ) + .from( su.getSourceAddress() ) + .to( su.getTargetAddress() ) + .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( 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( 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() ); + + updateSite( 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( 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( 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( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( gdcae.getTransmitChannel() ) + .details( ( gdcae.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdcae.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcae.getDownlinkFrequency() ) + .to( gdcae.getGroupAddress() ) + .build() ); + break; + } + } + + /** + * Process a traffic channel allocation message + */ + private void processTSBKChannelGrant( TSBKMessage message ) + { + P25CallEvent event = null; + + switch( message.getOpcode() ) + { + case GROUP_DATA_CHANNEL_GRANT: + GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( gdcg.getChannel() ) + .details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcg.getDownlinkFrequency() ) + .from( gdcg.getSourceAddress() ) + .to( gdcg.getGroupAddress() ) + .build(); + break; + case GROUP_VOICE_CHANNEL_GRANT: + GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcg.getChannel() ) + .details( "Trigger: TSBK-G_V_CH_GRNT " + + ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcg.getDownlinkFrequency() ) + .from( gvcg.getSourceAddress() ) + .to( gvcg.getGroupAddress() ) + .build(); + break; + case GROUP_VOICE_CHANNEL_GRANT_UPDATE: + GroupVoiceChannelGrantUpdate gvcgu = + (GroupVoiceChannelGrantUpdate)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcgu.getChannel1() ) + .details( "Trigger: TSBK-G_V_CH_GRNT_UP " + + ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency1() ) + .to( gvcgu.getGroupAddress1() ) + .build(); + + if( gvcgu.hasChannelNumber2() ) + { + /* We handle this channel grant independent of the first event */ + P25CallEvent event2 = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcgu.getChannel2() ) + .details( "Trigger: TSBK-G_V_CH_GRNT_UP#2 " + + ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency2() ) + .to( gvcgu.getGroupAddress2() ) + .build(); + + broadcast( new TrafficChannelAllocationEvent( this, event2 ) ); + } + break; + case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + GroupVoiceChannelGrantUpdateExplicit gvcgue = + (GroupVoiceChannelGrantUpdateExplicit)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcgue.getTransmitChannelIdentifier() + "-" + + gvcgue.getTransmitChannelNumber() ) + .details( "Trigger: TSBK-G_V_CH_GRNT_U_E " + + ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgue.getDownlinkFrequency() ) + .to( gvcgue.getGroupAddress() ) + .build(); + break; + case INDIVIDUAL_DATA_CHANNEL_GRANT: + IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( idcg.getChannel() ) + .details( "Trigger: TSBK-IND_D_CH_GRNT " + + ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( idcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( idcg.getDownlinkFrequency() ) + .from( idcg.getSourceAddress() ) + .to( idcg.getTargetAddress() ) + .build(); + break; + case SNDCP_DATA_CHANNEL_GRANT: + SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( sdcg.getTransmitChannel() ) + .details( "Trigger: TSBK-SN_D_CH_GRNT " + + "SNDCP DATA NSAPI:" + sdcg.getNSAPI() ) + .frequency( sdcg.getDownlinkFrequency() ) + .to( sdcg.getTargetAddress() ) + .build(); + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + TelephoneInterconnectVoiceChannelGrant tivcg = + (TelephoneInterconnectVoiceChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( getAliasList() ) + .channel( tivcg.getChannel() ) + .details( "Trigger: TSBK-T_INT_V_CH_GRNT " + + ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcg.isEmergency() ? " EMERGENCY" : "") + + " CALL TIMER:" + tivcg.getCallTimer() ) + .frequency( tivcg.getDownlinkFrequency() ) + .from( tivcg.getAddress() ) + .build(); + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = + (TelephoneInterconnectVoiceChannelGrantUpdate)message; + + event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( getAliasList() ) + .channel( tivcgu.getChannelIdentifier() + "-" + + tivcgu.getChannelNumber() ) + .details( "Trigger: TSBK-T_INT_V_CH_G_UP " + + ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( tivcgu.getDownlinkFrequency() ) + .from( tivcgu.getAddress() ) + .build(); + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + UnitToUnitVoiceChannelGrant uuvcg = + (UnitToUnitVoiceChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcg.getChannelIdentifier() + "-" + + uuvcg.getChannelNumber() ) + .details( "Trigger: TSBK-U2U_V_CH_GRNT " + + ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcg.getDownlinkFrequency() ) + .from( uuvcg.getSourceAddress() ) + .to( uuvcg.getTargetAddress() ) + .build(); + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + UnitToUnitVoiceChannelGrantUpdate uuvcgu = + (UnitToUnitVoiceChannelGrantUpdate)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcgu.getChannelIdentifier() + "-" + + uuvcgu.getChannelNumber() ) + .details( "Trigger: TSBK-U2U_V_CH_G_UP " + + ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgu.getDownlinkFrequency() ) + .from( uuvcgu.getSourceAddress() ) + .to( uuvcgu.getTargetAddress() ) + .build(); + break; + default: + break; + } + + if( event != null ) + { + broadcast( new TrafficChannelAllocationEvent( this, event ) ); + } + } + + /** + * Broadcasts an update for the TO group or unit address + */ + private void updateTo( String to ) + { + if( to != null && + !to.contentEquals( "0000" ) && + !to.contentEquals( "000000" ) && + ( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) ) + { + mToTalkgroup = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + + if( hasAliasList() ) + { + mToAlias = getAliasList().getTalkgroupAlias( to ); + } + else + { + mToAlias = null; + } + + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + } + + /** + * Broadcasts an update for the FROM group or unit address + */ + private void updateFrom( String from ) + { + if( from != null && + !from.contentEquals( "0000" ) && + !from.contentEquals( "000000" ) && + ( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) ) + { + mFromTalkgroup = from; + + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + if( hasAliasList() ) + { + mFromAlias = getAliasList().getTalkgroupAlias( from ); + } + else + { + mFromAlias = null; + } + + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + } + + /** + * Broadcasts an update to the NAC + */ + private void updateNAC( String nac ) + { + if( mNAC == null || ( nac != null && !mNAC.contentEquals( nac ) ) ) + { + mNAC = nac; + + broadcast( ChangedAttribute.NAC ); + } + } + + /** + * Updates the site information + */ + private void updateSite( String site ) + { + if( mSite == null || ( site != null && !mSite.contentEquals( site ) ) ) + { + mSite = site; + + broadcast( ChangedAttribute.SITE ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getSiteID( mSite ); + + if( alias != null ) + { + mSiteAlias = alias.getName(); + } + else + { + mSiteAlias = null; + } + } + + broadcast( ChangedAttribute.SITE_ALIAS ); + } + } + + private void updateSystem( String system ) + { + if( mSystem == null || ( system != null && !mSystem.contentEquals( system ) ) ) + { + mSystem = system; + + broadcast( ChangedAttribute.SYSTEM ); + } + } + + /** + * Process a unit paging event message + */ + private void processTSBKPage( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case CALL_ALERT: + CallAlert ca = (CallAlert)message; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getSourceID() ) + .to( ca.getTargetAddress() ) + .build() ); + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; + + if( mLastPageEventID == null || !mLastPageEventID + .contentEquals( utuar.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( 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( 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: + TelephoneInterconnectAnswerRequest tiar = + (TelephoneInterconnectAnswerRequest)message; + + if( mLastPageEventID == null || !mLastPageEventID + .contentEquals( tiar.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .details( ( "TELEPHONE INTERCONNECT" ) ) + .from( tiar.getTelephoneNumber() ) + .to( tiar.getTargetAddress() ) + .build() ); + + mLastPageEventID = tiar.getTargetAddress(); + } + break; + default: + break; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tP25\n" ); + sb.append( "===================== THIS SITE ======================" ); + + if( mNetworkStatus != null ) + { + sb.append( "\nNAC:\t" + mNetworkStatus.getNAC() ); + sb.append( "\nWACN-SYS:\t" + mNetworkStatus.getWACN() ); + sb.append( "-" + mNetworkStatus.getSystemID() ); + sb.append( " [" ); + sb.append( mNetworkStatus.getNetworkCallsign() ); + sb.append( "]" ); + sb.append( "\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea() ); + } + else if( mNetworkStatusExtended != null ) + { + sb.append( "\nNAC:\t" + mNetworkStatusExtended.getNAC() ); + sb.append( "\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN() ); + sb.append( "-" + mNetworkStatusExtended.getSystemID() ); + sb.append( " [" ); + sb.append( mNetworkStatusExtended.getNetworkCallsign() ); + sb.append( "]" ); + sb.append( "\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea() ); + } + + String site = null; + + if( mRFSSStatusMessage != null ) + { + site = mRFSSStatusMessage.getRFSubsystemID() + "-" + mRFSSStatusMessage.getSiteID(); + } + else if( mRFSSStatusMessageExtended != null ) + { + site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + + mRFSSStatusMessageExtended.getSiteID(); + } + + sb.append( "\nRFSS-SITE:\t" + site ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( site ); + + if( siteAlias != null ) + { + sb.append( " " + siteAlias.getName() ); + } + } + + if( mNetworkStatus != null ) + { + sb.append( "\nSERVICES:\t" + SystemService.toString( + mNetworkStatus.getSystemServiceClass() ) ); + sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)mNetworkStatus.getDownlinkFrequency() / 1E6d ) + + " [" + mNetworkStatus.getIdentifier() + "-" + + mNetworkStatus.getChannel() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mNetworkStatus.getUplinkFrequency() / 1E6d ) + " [" + + mNetworkStatus.getIdentifier() + "-" + + mNetworkStatus.getChannel() + "]\n" ); + } + else if( mNetworkStatusExtended != null ) + { + sb.append( "\nSERVICES:\t" + SystemService.toString( + mNetworkStatusExtended.getSystemServiceClass() ) ); + sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d ) + + " [" + mNetworkStatusExtended.getTransmitIdentifier() + "-" + + mNetworkStatusExtended.getTransmitChannel() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d ) + + " [" + mNetworkStatusExtended.getReceiveIdentifier() + "-" + + mNetworkStatusExtended.getReceiveChannel() + "]" ); + } + + if( mSecondaryControlChannels.isEmpty() ) + { + sb.append( "\nSCCH:\tNONE" ); + } + else + { + for( module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast + sec: mSecondaryControlChannels ) + { + sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)sec.getDownlinkFrequency1() / 1E6d ) + + " [" + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)sec.getUplinkFrequency1() / 1E6d ) + " [" + + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); + + if( sec.hasChannel2() ) + { + sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)sec.getDownlinkFrequency2() / 1E6d ) + + " [" + sec.getIdentifier2() + "-" + sec.getChannel2() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)sec.getUplinkFrequency2() / 1E6d ) + " [" + + sec.getIdentifier2() + "-" + sec.getChannel2() + "]" ); + } + } + } + + if( mSNDCPDataChannel != null ) + { + sb.append( "\nSNDCP:" ); + sb.append( "\tDNLINK " + mFrequencyFormatter.format( + (double)mSNDCPDataChannel.getDownlinkFrequency()/1E6D ) + + " [" + mSNDCPDataChannel.getTransmitChannel() + "]" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mSNDCPDataChannel.getUplinkFrequency()/1E6D ) + + " [" + mSNDCPDataChannel.getReceiveChannel() + "]" ); + } + + if( mProtectionParameterBroadcast != null ) + { + sb.append( "\nENCRYPTION:" ); + sb.append( "\nTYPE:\t" + mProtectionParameterBroadcast + .getEncryptionType().name() ); + sb.append( "\nALGORITHM:\t" + mProtectionParameterBroadcast + .getAlgorithmID() ); + sb.append( "\nKEY:\t" + mProtectionParameterBroadcast.getKeyID() ); + sb.append( "\nINBOUND IV:\t" + mProtectionParameterBroadcast + .getInboundInitializationVector() ); + sb.append( "\nOUTBOUND IV:\t" + mProtectionParameterBroadcast + .getOutboundInitializationVector() ); + } + + List identifiers = new ArrayList<>( mBands.keySet() ); + + Collections.sort( identifiers ); + + sb.append( "\nFREQUENCY BANDS:" ); + for( Integer id: identifiers ) + { + IBandIdentifier band = mBands.get( id ); + + sb.append( "\n\t" + 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\n=================== NEIGHBORS ======================" ); + + if( mNeighborMap.isEmpty() ) + { + sb.append( "\n\tNONE\n" ); + sb.append( "\n----------------------------------------------------" ); + } + else + { + for( AdjacentStatusBroadcast neighbor: mNeighborMap.values() ) + { + sb.append( "\nNAC:\t" + neighbor.getNAC() ); + sb.append( "\nSYSTEM:\t" + neighbor.getSystemID() ); + sb.append( "\nLRA:\t" + neighbor.getLocationRegistrationArea() ); + + String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); + sb.append( "\nRFSS-SITE:\t" + neighborID ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( neighborID ); + + if( siteAlias != null ) + { + sb.append( " " + siteAlias.getName() ); + } + } + + sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)neighbor.getDownlinkFrequency() / 1E6d ) + + " [" + neighbor.getIdentifier() + "-" + neighbor.getChannel() + "]" ); + sb.append( "\n\tUPLINK:" + mFrequencyFormatter.format( + (double)neighbor.getUplinkFrequency() / 1E6d ) + "\n" ); + sb.append( "\nSERVICES:\t" + Service.getServices( neighbor.getSystemServiceClass() ) ); + sb.append( "\n----------------------------------------------------" ); + } + } + + return sb.toString(); + } + + public String getNAC() + { + return mNAC; + } + + public String getSystem() + { + return mSystem; + } + + public String getSiteAlias() + { + return mSiteAlias; + } + + public String getSite() + { + return mSite; + } + + public String getFromTalkgroup() + { + return mFromTalkgroup; + } + + public Alias getFromAlias() + { + return mFromAlias; + } + + public String getToTalkgroup() + { + return mToTalkgroup; + } + + public Alias getToAlias() + { + return mToAlias; + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + 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; + + mCurrentChannel = allocationEvent.getCallEvent().getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + + mCurrentChannelFrequency = allocationEvent.getCallEvent().getFrequency(); + broadcast( ChangedAttribute.SOURCE ); + + mFromTalkgroup = allocationEvent.getCallEvent().getFromID(); + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mToTalkgroup = allocationEvent.getCallEvent().getToID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + } + break; + default: + break; + } + } +} diff --git a/src/decode/p25/P25Editor.java b/src/module/decode/p25/P25Editor.java similarity index 81% rename from src/decode/p25/P25Editor.java rename to src/module/decode/p25/P25Editor.java index f9e9f86e..347b235f 100644 --- a/src/decode/p25/P25Editor.java +++ b/src/module/decode/p25/P25Editor.java @@ -15,13 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.p25; +package module.decode.p25; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; + +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; +import module.decode.p25.P25Decoder.Modulation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,10 +35,6 @@ import source.SourceEditor; import source.tuner.TunerEditor; import controller.Editor; import controller.channel.ChannelValidationException; -import decode.DecodeEditor; -import decode.config.DecodeConfigP25Phase1; -import decode.config.DecodeConfiguration; -import decode.p25.P25Decoder.Modulation; public class P25Editor extends DecodeEditor { @@ -41,7 +42,7 @@ public class P25Editor extends DecodeEditor private static final long serialVersionUID = 1L; - private JComboBox mComboModulation; + private JComboBox mComboModulation; public P25Editor( DecodeConfiguration config ) { @@ -52,10 +53,10 @@ public class P25Editor extends DecodeEditor private void initGUI() { - mComboModulation = new JComboBox(); + mComboModulation = new JComboBox(); - mComboModulation.setModel( new DefaultComboBoxModel( - P25Decoder.Modulation.values() ) ); + mComboModulation.setModel( new DefaultComboBoxModel( + P25_LSMDecoder.Modulation.values() ) ); mComboModulation.setSelectedItem( ((DecodeConfigP25Phase1)mConfig).getModulation() ); @@ -64,7 +65,7 @@ public class P25Editor extends DecodeEditor @Override public void actionPerformed( ActionEvent e ) { - P25Decoder.Modulation selected = mComboModulation + P25_LSMDecoder.Modulation selected = mComboModulation .getItemAt( mComboModulation.getSelectedIndex() ); if( selected != null ) @@ -74,7 +75,8 @@ public class P25Editor extends DecodeEditor } } ); - add( mComboModulation, "span 2,wrap" ); + add( new JLabel( "Modulation:" ) ); + add( mComboModulation, "wrap" ); } /** diff --git a/src/decode/p25/P25Interleave.java b/src/module/decode/p25/P25Interleave.java similarity index 99% rename from src/decode/p25/P25Interleave.java rename to src/module/decode/p25/P25Interleave.java index 60da95eb..6c6ac746 100644 --- a/src/decode/p25/P25Interleave.java +++ b/src/module/decode/p25/P25Interleave.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import java.util.BitSet; diff --git a/src/decode/p25/P25MessageFramer.java b/src/module/decode/p25/P25MessageFramer.java similarity index 92% rename from src/decode/p25/P25MessageFramer.java rename to src/module/decode/p25/P25MessageFramer.java index fcb90263..32e41b21 100644 --- a/src/decode/p25/P25MessageFramer.java +++ b/src/module/decode/p25/P25MessageFramer.java @@ -1,8 +1,23 @@ -package decode.p25; +package module.decode.p25; import java.util.ArrayList; import message.Message; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.ldu.LDU2Message; +import module.decode.p25.message.ldu.lc.LDULCMessageFactory; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.pdu.PDUMessageFactory; +import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; +import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessageFactory; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.TDULCMessageFactory; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.TSBKMessageFactory; +import module.decode.p25.reference.DataUnitID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,21 +30,6 @@ import bits.ISyncDetectListener; import bits.MultiSyncPatternMatcher; import bits.SoftSyncDetector; import bits.SyncDetector; -import decode.p25.message.P25Message; -import decode.p25.message.hdu.HDUMessage; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.ldu.LDU2Message; -import decode.p25.message.ldu.lc.LDULCMessageFactory; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.pdu.PDUMessageFactory; -import decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import decode.p25.message.pdu.confirmed.PDUConfirmedMessageFactory; -import decode.p25.message.tdu.TDUMessage; -import decode.p25.message.tdu.lc.TDULCMessageFactory; -import decode.p25.message.tdu.lc.TDULinkControlMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.TSBKMessageFactory; -import decode.p25.reference.DataUnitID; import dsp.psk.CQPSKDemodulator; import dsp.psk.CQPSKDemodulator2; import dsp.symbol.Dibit; @@ -128,16 +128,19 @@ public class P25MessageFramer implements Listener { this( aliasList ); - /* For CQPSK, we include 3 additional sync detectors to watch for and - * correction +/-90 and 180 degree costas loop phase lock errors */ - mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CCW, - demodulator, PHASE_CORRECTION_90_DEGREES ) ); + if( demodulator != null ) + { + /* For CQPSK, we include 3 additional sync detectors to watch for and + * correction +/-90 and 180 degree costas loop phase lock errors */ + mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CCW, + demodulator, PHASE_CORRECTION_90_DEGREES ) ); - mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CW, - demodulator, -PHASE_CORRECTION_90_DEGREES ) ); + mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CW, + demodulator, -PHASE_CORRECTION_90_DEGREES ) ); - mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_180, - demodulator, PHASE_CORRECTION_180_DEGREES ) ); + mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_180, + demodulator, PHASE_CORRECTION_180_DEGREES ) ); + } } private void dispatch( Message message ) diff --git a/src/decode/p25/P25MessageProcessor.java b/src/module/decode/p25/P25MessageProcessor.java similarity index 78% rename from src/decode/p25/P25MessageProcessor.java rename to src/module/decode/p25/P25MessageProcessor.java index f23c064b..c7c8e597 100644 --- a/src/decode/p25/P25MessageProcessor.java +++ b/src/module/decode/p25/P25MessageProcessor.java @@ -1,8 +1,11 @@ -package decode.p25; +package module.decode.p25; import java.util.HashMap; import message.Message; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDUMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,16 +13,13 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; import alias.AliasList; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDUMessage; public class P25MessageProcessor implements Listener { private final static Logger mLog = LoggerFactory.getLogger( P25MessageProcessor.class ); - private Broadcaster mBroadcaster = new Broadcaster(); + 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 @@ -74,25 +74,25 @@ public class P25MessageProcessor implements Listener * 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( message.isValid() || message instanceof LDUMessage ) + if( mMessageListener != null && message.isValid() || message instanceof LDUMessage ) { - mBroadcaster.broadcast( message ); + mMessageListener.receive( message ); } } public void dispose() { mBandIdentifierMap.clear(); - mBroadcaster.dispose(); + mMessageListener = null; } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java new file mode 100644 index 00000000..4e425d86 --- /dev/null +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * 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 module.decode.p25; + +import instrument.tap.Tap; +import instrument.tap.stream.DibitTap; +import instrument.tap.stream.FloatTap; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import sample.real.RealBufferToStreamConverter; +import source.tuner.frequency.FrequencyCorrectionControl; +import alias.AliasList; +import controller.channel.Channel.ChannelType; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.gain.DirectGainControl; + +public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener +{ + private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); + + /* Instrumentation Taps */ + private static final String INSTRUMENT_FILTER_OUTPUT = "Tap Point: Pre-Filter Output"; + private static final String INSTRUMENT_DGC_OUTPUT = "Tap Point: DGC Output"; + private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; + private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; + + private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- + + private List mAvailableTaps; + private RealFIRFilter_RB_RB mC4FMPreFilter; + private DirectGainControl mDGC; + private RealBufferToStreamConverter mStreamConverter = + new RealBufferToStreamConverter(); + private FrequencyCorrectionControl mFrequencyCorrectionControl; + private C4FMSymbolFilter mSymbolFilter; + private C4FMSlicer mC4FMSlicer; + private P25MessageFramer mMessageFramer; + + /** + * P25 Phase 1 C4FM Decoder processes real buffers of un-filtered, + * demodulated audio and produces decoded P25 Phase 1 Messages. + * + * Provides Frequency Control to steer external tuner source and incorporates + * internal gain control. + * + * Note: use the P25AudioModule to convert the decoded messages into audio. + * + * @param aliasList - optional (can be null) list of alias values for network + * infrastructure and subscriber identities that will be included in each + * decoded message + */ + public P25_C4FMDecoder( AliasList aliasList, ChannelType channelType ) + { + super( aliasList, channelType ); + + /* Filter demodulated sample buffers */ + mC4FMPreFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( + 48000, 6750, 7500, 60, WindowType.HANNING, true ), 1.0f ); + + /* Convert buffers to a sample stream so that the symbol filter can + * provide fine-grained gain control over individual samples */ + mC4FMPreFilter.setListener( mStreamConverter ); + + /* Apply gain remotely controlled by the downstream symbol filter */ + mDGC = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); + mStreamConverter.setListener( mDGC ); + + /* Issue tuned frequency correction commands, remotely controlled by the + * downstream symbol filter */ + mFrequencyCorrectionControl = new FrequencyCorrectionControl( + MAXIMUM_FREQUENCY_CORRECTION ); + + /* Shape gain and frequency offsets to optimize sample stream */ + mSymbolFilter = new C4FMSymbolFilter( mDGC, mFrequencyCorrectionControl ); + mDGC.setListener( mSymbolFilter ); + + /* Convert samples to symbols */ + mC4FMSlicer = new C4FMSlicer(); + mSymbolFilter.setListener( mC4FMSlicer ); + + /* Sync pattern detection and message construction */ + mMessageFramer = new P25MessageFramer( aliasList ); + mC4FMSlicer.addListener( mMessageFramer ); + + /* Process and broadcast messages */ + mMessageFramer.setListener( getMessageProcessor() ); + } + + public Modulation getModulation() + { + return Modulation.C4FM; + } + + @Override + public boolean hasFrequencyCorrectionControl() + { + return mFrequencyCorrectionControl != null; + } + + @Override + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return mFrequencyCorrectionControl; + } + + @Override + public Listener getRealBufferListener() + { + return mC4FMPreFilter; + } + + /** + * Instrumentation taps for monitoring internal processing + */ + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.add( new FloatTap( INSTRUMENT_FILTER_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new FloatTap( INSTRUMENT_DGC_OUTPUT, 0, 1.0f ) ); + + if( mSymbolFilter != null ) + { + mAvailableTaps.addAll( mSymbolFilter.getTaps() ); + } + + mAvailableTaps.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); + mAvailableTaps.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); + } + + return mAvailableTaps; + } + + /** + * Adds an instrumentation tap to monitor internal processing. + */ + @Override + public void addTap( Tap tap ) + { + if( mSymbolFilter != null ) + { + mSymbolFilter.addTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_FILTER_OUTPUT: + FloatTap filterOutput = (FloatTap)tap; + mStreamConverter.setListener( filterOutput ); + filterOutput.setListener( mDGC ); + break; + case INSTRUMENT_DGC_OUTPUT: + FloatTap dgcTap = (FloatTap)tap; + mDGC.setListener( dgcTap ); + dgcTap.setListener( mSymbolFilter ); + break; + case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: + FloatTap symbolTap = (FloatTap)tap; + mSymbolFilter.setListener( symbolTap ); + symbolTap.setListener( mC4FMSlicer ); + break; + case INSTRUMENT_C4FM_SLICER_OUTPUT: + DibitTap slicerTap = (DibitTap)tap; + + if( mC4FMSlicer != null ) + { + mC4FMSlicer.addListener( slicerTap ); + } + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } + + /** + * Removes the instrumentation tap. + */ + @Override + public void removeTap( Tap tap ) + { + if( mSymbolFilter != null ) + { + mSymbolFilter.removeTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_FILTER_OUTPUT: + mStreamConverter.setListener( mDGC ); + break; + case INSTRUMENT_DGC_OUTPUT: + mDGC.setListener( mSymbolFilter ); + break; + case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: + mSymbolFilter.setListener( mC4FMSlicer ); + break; + case INSTRUMENT_C4FM_SLICER_OUTPUT: + DibitTap slicerTap = (DibitTap)tap; + + if( mC4FMSlicer != null ) + { + mC4FMSlicer.removeListener( slicerTap ); + } + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } +} diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java new file mode 100644 index 00000000..60999e97 --- /dev/null +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * 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 module.decode.p25; + +import instrument.tap.Tap; +import instrument.tap.stream.ComplexSampleTap; +import instrument.tap.stream.ComplexTap; +import instrument.tap.stream.DibitTap; +import instrument.tap.stream.FloatTap; +import instrument.tap.stream.QPSKTap; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import controller.channel.Channel.ChannelType; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.ComplexBufferToStreamConverter; +import sample.complex.IComplexBufferListener; +import source.tuner.frequency.FrequencyCorrectionControl; +import alias.AliasList; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.gain.ComplexFeedForwardGainControl; +import dsp.psk.CQPSKDemodulator; +import dsp.psk.QPSKPolarSlicer; + +public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener +{ + private final static Logger mLog = LoggerFactory.getLogger( P25_LSMDecoder.class ); + + /* Instrumentation Taps */ + private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; + private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; + private static final String INSTRUMENT_QPSK_DEMODULATOR_OUTPUT = "Tap Point: CQPSK Demodulator Output"; + private static final String INSTRUMENT_CQPSK_SLICER_OUTPUT = "Tap Point: CQPSK Slicer Output"; + + private List mAvailableTaps; + + private ComplexFIRFilter_CB_CB mBasebandFilter; + private ComplexBufferToStreamConverter mStreamConverter = + new ComplexBufferToStreamConverter(); + private ComplexFeedForwardGainControl mAGC = + new ComplexFeedForwardGainControl( 32 ); + private CQPSKDemodulator mCQPSKDemodulator = new CQPSKDemodulator(); + private QPSKPolarSlicer mCQPSKSlicer = new QPSKPolarSlicer(); + private P25MessageFramer mMessageFramer; + + public P25_LSMDecoder( AliasList aliasList, ChannelType channelType ) + { + super( aliasList, channelType ); + + mBasebandFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( + 48000, 7250, 8000, 60, WindowType.HANNING, true ), 1.0f ); + + mBasebandFilter.setListener( mStreamConverter ); + + mStreamConverter.setListener( mAGC ); + + mAGC.setListener( mCQPSKDemodulator ); + + mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + + mCQPSKSlicer.addListener( mMessageFramer ); + + mMessageFramer = new P25MessageFramer( aliasList, mCQPSKDemodulator ); + mCQPSKSlicer.addListener( mMessageFramer ); + + mMessageFramer.setListener( getMessageProcessor() ); + } + + @Override + public Listener getComplexBufferListener() + { + return mBasebandFilter; + } + + public Modulation getModulation() + { + return Modulation.CQPSK; + } + + @Override + public boolean hasFrequencyCorrectionControl() + { + return false; + } + + @Override + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return null; + } + + /** + * Provides a list of instrumentation taps for monitoring internal processing + */ + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new QPSKTap( INSTRUMENT_QPSK_DEMODULATOR_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new DibitTap( INSTRUMENT_CQPSK_SLICER_OUTPUT, 0, 0.1f ) ); + + if( mCQPSKDemodulator != null ) + { + mAvailableTaps.addAll( mCQPSKDemodulator.getTaps() ); + } + } + + return mAvailableTaps; + } + + /** + * Adds the instrumentation tap + */ + @Override + public void addTap( Tap tap ) + { + if( mCQPSKDemodulator != null ) + { + mCQPSKDemodulator.addTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_BASEBAND_FILTER_OUTPUT: + ComplexSampleTap baseband = (ComplexSampleTap)tap; + mStreamConverter.setListener( baseband ); + baseband.setListener( mAGC ); + break; + case INSTRUMENT_AGC_OUTPUT: + ComplexSampleTap agcSymbol = (ComplexSampleTap)tap; + mAGC.setListener( agcSymbol ); + agcSymbol.setListener( mCQPSKDemodulator ); + break; + case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: + QPSKTap qpsk = (QPSKTap)tap; + mCQPSKDemodulator.setSymbolListener( qpsk ); + qpsk.setListener( mCQPSKSlicer ); + break; + case INSTRUMENT_CQPSK_SLICER_OUTPUT: + mCQPSKSlicer.addListener( (DibitTap)tap ); + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } + + /** + * Removes the instrumentation tap + */ + @Override + public void removeTap( Tap tap ) + { + if( mCQPSKDemodulator != null ) + { + mCQPSKDemodulator.removeTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_BASEBAND_FILTER_OUTPUT: + mStreamConverter.setListener( mAGC ); + break; + case INSTRUMENT_AGC_OUTPUT: + mAGC.setListener( mCQPSKDemodulator ); + break; + case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: + mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + break; + case INSTRUMENT_CQPSK_SLICER_OUTPUT: + mCQPSKSlicer.removeListener( (DibitTap)tap ); + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } +} diff --git a/src/decode/p25/Trellis_1_2_Rate.java b/src/module/decode/p25/Trellis_1_2_Rate.java similarity index 99% rename from src/decode/p25/Trellis_1_2_Rate.java rename to src/module/decode/p25/Trellis_1_2_Rate.java index 3bf37f63..ee35c5ad 100644 --- a/src/decode/p25/Trellis_1_2_Rate.java +++ b/src/module/decode/p25/Trellis_1_2_Rate.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import java.util.ArrayList; import java.util.Random; diff --git a/src/decode/p25/Trellis_3_4_Rate.java b/src/module/decode/p25/Trellis_3_4_Rate.java similarity index 99% rename from src/decode/p25/Trellis_3_4_Rate.java rename to src/module/decode/p25/Trellis_3_4_Rate.java index 0f1ed03a..4cca9cf7 100644 --- a/src/decode/p25/Trellis_3_4_Rate.java +++ b/src/module/decode/p25/Trellis_3_4_Rate.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java new file mode 100644 index 00000000..140ee98b --- /dev/null +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -0,0 +1,220 @@ +package module.decode.p25.audio; + +import jmbe.iface.AudioConversionLibrary; +import jmbe.iface.AudioConverter; +import message.IMessageListener; +import message.Message; +import module.Module; +import module.decode.p25.message.ldu.LDUMessage; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import controller.channel.ChannelEvent; +import controller.channel.IChannelEventListener; +import sample.Listener; +import alias.Metadata; +import audio.AudioFormats; +import audio.AudioPacket; +import audio.IAudioPacketProvider; +import audio.metadata.AudioMetadata; +import audio.metadata.IMetadataListener; + +public class P25AudioModule extends Module implements Listener, + IAudioPacketProvider, IMessageListener, IChannelEventListener, IMetadataListener +{ + private final static Logger mLog = LoggerFactory.getLogger( P25AudioModule.class ); + + private static final String IMBE_CODEC = "IMBE"; + + /* Provides a unique identifier for this audio module instance to use as a + * source identifier for all audio packets */ + private static int UNIQUE_ID = 0; + private int mSourceID; + private static boolean mLibraryLoadStatusLogged = false; + private boolean mCanConvertAudio = false; + private boolean mEncryptedAudio = false; + private AudioConverter mAudioConverter; + private Listener mAudioPacketListener; + private AudioMetadata mAudioMetadata; + private ChannelEventListener mChannelEventListener = new ChannelEventListener(); + + public P25AudioModule() + { + mSourceID = ++UNIQUE_ID; + mAudioMetadata = new AudioMetadata( mSourceID ); + } + + @Override + public Listener getMessageListener() + { + return this; + } + + @Override + public void dispose() + { + mAudioConverter = null; + } + + @Override + public void init() + { + loadConverter(); + } + + /** + * Primary inject point for p25 imbe audio frame messages. Each LDU audio + * message contains 9 imbe voice frames. If the audio is not encrypted, + * we insert each audio frame asynchronously into the audio converter. + */ + public void receive( Message message ) + { + if( mCanConvertAudio && mAudioPacketListener != null ) + { + if( message instanceof LDUMessage ) + { + LDUMessage ldu = (LDUMessage)message; + + /* If we detect a valid LDU message with the encryption flag + * set to true, we toggle the encrypted audio state until we + * receive the first TDU or TDULC message, then reset it. */ + if( ldu.isValid() && ldu.isEncrypted() ) + { + mEncryptedAudio = true; + } + + if( !mEncryptedAudio && mAudioConverter != null ) + { + for( byte[] frame: ((LDUMessage)message).getIMBEFrames() ) + { + float[] audio = mAudioConverter.convert( frame ); + + mAudioPacketListener.receive( + new AudioPacket( audio, mAudioMetadata ) ); + } + } + } + else if( message instanceof TDUMessage || message instanceof TDULinkControlMessage ) + { + mEncryptedAudio = false; + } + } + } + + /** + * Loads audio frame processing chain. Constructs an imbe targetdataline + * to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion + * stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper. + */ + private void loadConverter() + { + AudioConversionLibrary library = null; + + try + { + @SuppressWarnings( "rawtypes" ) + Class temp = Class.forName( "jmbe.JMBEAudioLibrary" ); + + library = (AudioConversionLibrary)temp.newInstance(); + + mAudioConverter = library.getAudioConverter( IMBE_CODEC, + AudioFormats.PCM_SIGNED_48KHZ_16BITS ); + + if( mAudioConverter != null ) + { + mCanConvertAudio = true; + + if( !mLibraryLoadStatusLogged ) + { + mLog.info( "JMBE audio conversion library successfully loaded" + + " - P25 audio will be available" ); + + mLibraryLoadStatusLogged = true; + } + } + else + { + if( !mLibraryLoadStatusLogged ) + { + mLog.info( "JMBE audio conversion library NOT FOUND" ); + mLibraryLoadStatusLogged = true; + } + } + } + catch ( ClassNotFoundException e1 ) + { + if( !mLibraryLoadStatusLogged ) + { + mLog.error( "Couldn't find/load JMBE audio conversion library" ); + mLibraryLoadStatusLogged = true; + } + } + catch ( InstantiationException e1 ) + { + if( !mLibraryLoadStatusLogged ) + { + mLog.error( "Couldn't instantiate JMBE audio conversion library class" ); + mLibraryLoadStatusLogged = true; + } + } + catch ( IllegalAccessException e1 ) + { + if( !mLibraryLoadStatusLogged ) + { + mLog.error( "Couldn't load JMBE audio conversion library due to " + + "security restrictions" ); + mLibraryLoadStatusLogged = true; + } + } + } + + @Override + public void setAudioPacketListener( Listener listener ) + { + mAudioPacketListener = listener; + } + + @Override + public void removeAudioPacketListener() + { + mAudioPacketListener = null; + } + + @Override + public Listener getMetadataListener() + { + return mAudioMetadata; + } + + @Override + public Listener getChannelEventListener() + { + return mChannelEventListener; + } + + /** + * Wrapper for channel event listener. Responds to channel state reset + * events to remove/cleanup current audio metadata + */ + public class ChannelEventListener implements Listener + { + @Override + public void receive( ChannelEvent event ) + { + switch( event.getEvent() ) + { + case CHANNEL_STATE_RESET: + mAudioMetadata.reset(); + break; + case CHANGE_SELECTED: + mAudioMetadata.setSelected( event.getChannel().isSelected() ); + break; + default: + break; + } + } + } +} diff --git a/src/decode/p25/message/IBandIdentifier.java b/src/module/decode/p25/message/IBandIdentifier.java similarity index 92% rename from src/decode/p25/message/IBandIdentifier.java rename to src/module/decode/p25/message/IBandIdentifier.java index ad995510..a2588c42 100644 --- a/src/decode/p25/message/IBandIdentifier.java +++ b/src/module/decode/p25/message/IBandIdentifier.java @@ -1,4 +1,4 @@ -package decode.p25.message; +package module.decode.p25.message; public interface IBandIdentifier { diff --git a/src/decode/p25/message/IdentifierReceiver.java b/src/module/decode/p25/message/IdentifierReceiver.java similarity index 91% rename from src/decode/p25/message/IdentifierReceiver.java rename to src/module/decode/p25/message/IdentifierReceiver.java index 0438ae1b..a1c65c63 100644 --- a/src/decode/p25/message/IdentifierReceiver.java +++ b/src/module/decode/p25/message/IdentifierReceiver.java @@ -1,4 +1,4 @@ -package decode.p25.message; +package module.decode.p25.message; /** diff --git a/src/decode/p25/message/P25Message.java b/src/module/decode/p25/message/P25Message.java similarity index 97% rename from src/decode/p25/message/P25Message.java rename to src/module/decode/p25/message/P25Message.java index 69f20486..e8421211 100644 --- a/src/decode/p25/message/P25Message.java +++ b/src/module/decode/p25/message/P25Message.java @@ -1,13 +1,13 @@ -package decode.p25.message; +package module.decode.p25.message; import java.text.SimpleDateFormat; import map.Plottable; import message.Message; +import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; import edac.CRC; public class P25Message extends Message diff --git a/src/decode/p25/message/filter/HDUMessageFilter.java b/src/module/decode/p25/message/filter/HDUMessageFilter.java similarity index 79% rename from src/decode/p25/message/filter/HDUMessageFilter.java rename to src/module/decode/p25/message/filter/HDUMessageFilter.java index 78c5deee..0f716691 100644 --- a/src/decode/p25/message/filter/HDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/HDUMessageFilter.java @@ -1,11 +1,11 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.hdu.HDUMessage; -import decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/LDUMessageFilter.java b/src/module/decode/p25/message/filter/LDUMessageFilter.java similarity index 86% rename from src/decode/p25/message/filter/LDUMessageFilter.java rename to src/module/decode/p25/message/filter/LDUMessageFilter.java index 85a95a1f..d0fcfb52 100644 --- a/src/decode/p25/message/filter/LDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/LDUMessageFilter.java @@ -1,10 +1,10 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.ldu.LDUMessage; +import module.decode.p25.message.ldu.LDUMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/P25MessageFilterSet.java b/src/module/decode/p25/message/filter/P25MessageFilterSet.java similarity index 86% rename from src/decode/p25/message/filter/P25MessageFilterSet.java rename to src/module/decode/p25/message/filter/P25MessageFilterSet.java index cb93aaba..f4f72258 100644 --- a/src/decode/p25/message/filter/P25MessageFilterSet.java +++ b/src/module/decode/p25/message/filter/P25MessageFilterSet.java @@ -1,7 +1,7 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import message.Message; -import decode.p25.message.P25Message; +import module.decode.p25.message.P25Message; import filter.FilterSet; public class P25MessageFilterSet extends FilterSet diff --git a/src/decode/p25/message/filter/PDUMessageFilter.java b/src/module/decode/p25/message/filter/PDUMessageFilter.java similarity index 89% rename from src/decode/p25/message/filter/PDUMessageFilter.java rename to src/module/decode/p25/message/filter/PDUMessageFilter.java index 42fdd002..a18bd47d 100644 --- a/src/decode/p25/message/filter/PDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/PDUMessageFilter.java @@ -1,12 +1,12 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import message.Message; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.Opcode; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.Opcode; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/TDULCMessageFilter.java b/src/module/decode/p25/message/filter/TDULCMessageFilter.java similarity index 80% rename from src/decode/p25/message/filter/TDULCMessageFilter.java rename to src/module/decode/p25/message/filter/TDULCMessageFilter.java index 43473a3f..dbf38811 100644 --- a/src/decode/p25/message/filter/TDULCMessageFilter.java +++ b/src/module/decode/p25/message/filter/TDULCMessageFilter.java @@ -1,11 +1,11 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.tdu.TDUMessage; -import decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/TDUMessageFilter.java b/src/module/decode/p25/message/filter/TDUMessageFilter.java similarity index 86% rename from src/decode/p25/message/filter/TDUMessageFilter.java rename to src/module/decode/p25/message/filter/TDUMessageFilter.java index d7f2a20b..f87dc835 100644 --- a/src/decode/p25/message/filter/TDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/TDUMessageFilter.java @@ -1,10 +1,10 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.TDUMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/TSBKMessageFilterSet.java b/src/module/decode/p25/message/filter/TSBKMessageFilterSet.java similarity index 93% rename from src/decode/p25/message/filter/TSBKMessageFilterSet.java rename to src/module/decode/p25/message/filter/TSBKMessageFilterSet.java index 1b90bb54..337c0fb0 100644 --- a/src/decode/p25/message/filter/TSBKMessageFilterSet.java +++ b/src/module/decode/p25/message/filter/TSBKMessageFilterSet.java @@ -1,15 +1,15 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import message.Message; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.message.tsbk.vendor.VendorOpcode; -import decode.p25.reference.Opcode; -import decode.p25.reference.Vendor; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.message.tsbk.vendor.VendorOpcode; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Vendor; import filter.Filter; import filter.FilterElement; import filter.FilterSet; diff --git a/src/decode/p25/message/hdu/HDUMessage.java b/src/module/decode/p25/message/hdu/HDUMessage.java similarity index 98% rename from src/decode/p25/message/hdu/HDUMessage.java rename to src/module/decode/p25/message/hdu/HDUMessage.java index 857658f2..2e7d5da3 100644 --- a/src/decode/p25/message/hdu/HDUMessage.java +++ b/src/module/decode/p25/message/hdu/HDUMessage.java @@ -1,13 +1,14 @@ -package decode.p25.message.hdu; +package module.decode.p25.message.hdu; + +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; import edac.CRC; import edac.Golay18; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/ldu/LDU1Message.java b/src/module/decode/p25/message/ldu/LDU1Message.java similarity index 97% rename from src/decode/p25/message/ldu/LDU1Message.java rename to src/module/decode/p25/message/ldu/LDU1Message.java index b9a19f4c..929bac1e 100644 --- a/src/decode/p25/message/ldu/LDU1Message.java +++ b/src/module/decode/p25/message/ldu/LDU1Message.java @@ -1,14 +1,15 @@ -package decode.p25.message.ldu; +package module.decode.p25.message.ldu; + +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Vendor; +import module.decode.p25.reference.VendorLinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Vendor; -import decode.p25.reference.VendorLinkControlOpcode; import edac.CRC; import edac.Hamming10; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/ldu/LDU2Message.java b/src/module/decode/p25/message/ldu/LDU2Message.java similarity index 98% rename from src/decode/p25/message/ldu/LDU2Message.java rename to src/module/decode/p25/message/ldu/LDU2Message.java index 946061fd..776a33c7 100644 --- a/src/decode/p25/message/ldu/LDU2Message.java +++ b/src/module/decode/p25/message/ldu/LDU2Message.java @@ -1,12 +1,13 @@ -package decode.p25.message.ldu; +package module.decode.p25.message.ldu; + +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; import edac.CRC; import edac.Hamming10; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/ldu/LDUMessage.java b/src/module/decode/p25/message/ldu/LDUMessage.java similarity index 95% rename from src/decode/p25/message/ldu/LDUMessage.java rename to src/module/decode/p25/message/ldu/LDUMessage.java index cb8c432c..2d67e4f8 100644 --- a/src/decode/p25/message/ldu/LDUMessage.java +++ b/src/module/decode/p25/message/ldu/LDUMessage.java @@ -1,15 +1,16 @@ -package decode.p25.message.ldu; +package module.decode.p25.message.ldu; import java.util.ArrayList; import java.util.List; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.reference.DataUnitID; public abstract class LDUMessage extends P25Message { diff --git a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java rename to src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java index 9e2d49f2..b123ee9d 100644 --- a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class AdjacentSiteStatusBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java similarity index 93% rename from src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java index 2b5d6628..16c3e056 100644 --- a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/CallAlert.java b/src/module/decode/p25/message/ldu/lc/CallAlert.java similarity index 89% rename from src/decode/p25/message/ldu/lc/CallAlert.java rename to src/module/decode/p25/message/ldu/lc/CallAlert.java index b564d230..731fc4ba 100644 --- a/src/decode/p25/message/ldu/lc/CallAlert.java +++ b/src/module/decode/p25/message/ldu/lc/CallAlert.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class CallAlert extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/CallTermination.java b/src/module/decode/p25/message/ldu/lc/CallTermination.java similarity index 85% rename from src/decode/p25/message/ldu/lc/CallTermination.java rename to src/module/decode/p25/message/ldu/lc/CallTermination.java index 8eaeace1..2d69f791 100644 --- a/src/decode/p25/message/ldu/lc/CallTermination.java +++ b/src/module/decode/p25/message/ldu/lc/CallTermination.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class CallTermination extends LDU1Message { private final static Logger mLog = LoggerFactory.getLogger( CallTermination.class ); diff --git a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java similarity index 91% rename from src/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java rename to src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java index 1adf1b34..8fa255ec 100644 --- a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdate extends LDU1Message implements IBandIdentifier { diff --git a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java rename to src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java index 18ebe8a6..c924e693 100644 --- a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdateExplicit extends LDU1Message implements IBandIdentifier { diff --git a/src/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java b/src/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java similarity index 91% rename from src/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java rename to src/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java index cee555f0..28b925ea 100644 --- a/src/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java +++ b/src/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.ExtendedFunction; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.ExtendedFunction; -import decode.p25.reference.LinkControlOpcode; - public class ExtendedFunctionCommand extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/GroupAffiliationQuery.java b/src/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java similarity index 88% rename from src/decode/p25/message/ldu/lc/GroupAffiliationQuery.java rename to src/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java index 22100c83..5f0a8b4a 100644 --- a/src/decode/p25/message/ldu/lc/GroupAffiliationQuery.java +++ b/src/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupAffiliationQuery extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java similarity index 93% rename from src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java rename to src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java index c4670f49..4091664b 100644 --- a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdate extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java rename to src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java index c9555ba3..cd946525 100644 --- a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java @@ -1,15 +1,15 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdateExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java similarity index 93% rename from src/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java rename to src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java index bc5578ac..c950bcc8 100644 --- a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUser extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/LDULCMessageFactory.java b/src/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java similarity index 95% rename from src/decode/p25/message/ldu/lc/LDULCMessageFactory.java rename to src/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java index e12871a8..f6e053a1 100644 --- a/src/decode/p25/message/ldu/lc/LDULCMessageFactory.java +++ b/src/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.Vendor; - public class LDULCMessageFactory { diff --git a/src/decode/p25/message/ldu/lc/MessageUpdate.java b/src/module/decode/p25/message/ldu/lc/MessageUpdate.java similarity index 91% rename from src/decode/p25/message/ldu/lc/MessageUpdate.java rename to src/module/decode/p25/message/ldu/lc/MessageUpdate.java index 1af4bc29..bfcc239d 100644 --- a/src/decode/p25/message/ldu/lc/MessageUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/MessageUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class MessageUpdate extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java similarity index 89% rename from src/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java rename to src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java index 0e4ab8a8..ec22b3ff 100644 --- a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class NetworkStatusBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java similarity index 92% rename from src/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java index cbc6f51a..682882ff 100644 --- a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class NetworkStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java b/src/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java similarity index 88% rename from src/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java rename to src/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java index fdae5e3a..2cf1c570 100644 --- a/src/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.Encryption; -import decode.p25.reference.LinkControlOpcode; - public class ProtectionParameterBroadcast extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java similarity index 89% rename from src/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java rename to src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java index 501a2c86..07edbef1 100644 --- a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java index e49a363c..b3b7b7ec 100644 --- a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java similarity index 91% rename from src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java rename to src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java index 81342a55..e154f8d5 100644 --- a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java index 60433ff1..299531b2 100644 --- a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcastExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/StatusQuery.java b/src/module/decode/p25/message/ldu/lc/StatusQuery.java similarity index 88% rename from src/decode/p25/message/ldu/lc/StatusQuery.java rename to src/module/decode/p25/message/ldu/lc/StatusQuery.java index 41637685..c00c29e6 100644 --- a/src/decode/p25/message/ldu/lc/StatusQuery.java +++ b/src/module/decode/p25/message/ldu/lc/StatusQuery.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class StatusQuery extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/StatusUpdate.java b/src/module/decode/p25/message/ldu/lc/StatusUpdate.java similarity index 91% rename from src/decode/p25/message/ldu/lc/StatusUpdate.java rename to src/module/decode/p25/message/ldu/lc/StatusUpdate.java index 08c83342..ceced2df 100644 --- a/src/decode/p25/message/ldu/lc/StatusUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/StatusUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class StatusUpdate extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/SystemServiceBroadcast.java b/src/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java similarity index 88% rename from src/decode/p25/message/ldu/lc/SystemServiceBroadcast.java rename to src/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java index d7679ecb..c030f00d 100644 --- a/src/decode/p25/message/ldu/lc/SystemServiceBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; import java.util.List; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class SystemServiceBroadcast extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java similarity index 92% rename from src/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java rename to src/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java index c70c7796..c035c931 100644 --- a/src/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java +++ b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java @@ -1,15 +1,15 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; import java.util.ArrayList; import java.util.List; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.Digit; +import module.decode.p25.reference.LinkControlOpcode; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.Digit; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectAnswerRequest extends LDU1Message { private final static Logger mLog = LoggerFactory.getLogger( diff --git a/src/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java similarity index 89% rename from src/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java rename to src/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java index 00e6a04a..af1fe0a7 100644 --- a/src/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java +++ b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectVoiceChannelUser extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java b/src/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java similarity index 91% rename from src/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java rename to src/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java index 2e0cf1e2..6b4843aa 100644 --- a/src/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java +++ b/src/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitAuthenticationCommand extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitRegistrationCommand.java b/src/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java similarity index 91% rename from src/decode/p25/message/ldu/lc/UnitRegistrationCommand.java rename to src/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java index 50c2b559..34c37c34 100644 --- a/src/decode/p25/message/ldu/lc/UnitRegistrationCommand.java +++ b/src/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitRegistrationCommand extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java b/src/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java similarity index 88% rename from src/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java rename to src/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java index 26a0fd70..9309578f 100644 --- a/src/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java +++ b/src/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitAnswerRequest extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java b/src/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java similarity index 88% rename from src/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java rename to src/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java index 37b582c4..383cb9b7 100644 --- a/src/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java +++ b/src/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitVoiceChannelUser extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java b/src/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java similarity index 88% rename from src/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java rename to src/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java index f15cc939..f9e1a6fc 100644 --- a/src/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java +++ b/src/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc.motorola; +package module.decode.p25.message.ldu.lc.motorola; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class MotorolaOpcode_0 extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/pdu/PDUMessage.java b/src/module/decode/p25/message/pdu/PDUMessage.java similarity index 91% rename from src/decode/p25/message/pdu/PDUMessage.java rename to src/module/decode/p25/message/pdu/PDUMessage.java index 18b17e8c..78c703ad 100644 --- a/src/decode/p25/message/pdu/PDUMessage.java +++ b/src/module/decode/p25/message/pdu/PDUMessage.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu; +package module.decode.p25.message.pdu; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.tsbk.vendor.VendorOpcode; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.PDUFormat; +import module.decode.p25.reference.ServiceAccessPoint; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.message.tsbk.vendor.VendorOpcode; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.PDUFormat; -import decode.p25.reference.ServiceAccessPoint; -import decode.p25.reference.Vendor; import edac.CRC; public class PDUMessage extends P25Message diff --git a/src/decode/p25/message/pdu/PDUMessageFactory.java b/src/module/decode/p25/message/pdu/PDUMessageFactory.java similarity index 66% rename from src/decode/p25/message/pdu/PDUMessageFactory.java rename to src/module/decode/p25/message/pdu/PDUMessageFactory.java index aa831079..bafef26b 100644 --- a/src/decode/p25/message/pdu/PDUMessageFactory.java +++ b/src/module/decode/p25/message/pdu/PDUMessageFactory.java @@ -1,31 +1,31 @@ -package decode.p25.message.pdu; +package module.decode.p25.message.pdu; +import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.CallAlertExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; +import module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; +import module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; +import module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; +import module.decode.p25.message.pdu.osp.control.StatusQueryExtended; +import module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; +import module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; +import module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantUpdateExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.PDUFormat; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.CallAlertExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import decode.p25.message.pdu.osp.control.StatusQueryExtended; -import decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantUpdateExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.PDUFormat; -import decode.p25.reference.Vendor; public class PDUMessageFactory { diff --git a/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java b/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java similarity index 95% rename from src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java index e5c0c03c..d54c1011 100644 --- a/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu; +package module.decode.p25.message.pdu; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public abstract class UnitToUnitChannelGrantExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java similarity index 95% rename from src/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java rename to src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java index 3ee3ad8c..174c9466 100644 --- a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java +++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java @@ -1,14 +1,15 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.PDUType; +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.PDUType; -import decode.p25.reference.Vendor; import edac.CRC; import edac.CRCP25; diff --git a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java similarity index 97% rename from src/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java rename to src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java index 379f09de..b695dd79 100644 --- a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java +++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java @@ -1,4 +1,4 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java similarity index 93% rename from src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java index fa47e6c7..d6ddbd4b 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java @@ -1,16 +1,17 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.MDPConfigurationOption; +import module.decode.p25.reference.PDUType; +import module.decode.p25.reference.SNDCPActivationRejectReason; +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.MDPConfigurationOption; -import decode.p25.reference.PDUType; -import decode.p25.reference.SNDCPActivationRejectReason; -import decode.p25.reference.Vendor; import edac.CRC; import edac.CRCP25; diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java similarity index 92% rename from src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java index f8d0ec8b..e9a56414 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.SNDCPActivationRejectReason; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.SNDCPActivationRejectReason; - public class SNDCPActivateTDSContextReject extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java similarity index 97% rename from src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java index 194087ce..ba7966dd 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.MDPConfigurationOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.MDPConfigurationOption; - public class SNDCPActivateTDSContextRequest extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java similarity index 89% rename from src/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java index c8d0b778..000b4f0a 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.PDUType; +import module.decode.p25.reference.SNDCPActivationRejectReason; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.PDUType; -import decode.p25.reference.SNDCPActivationRejectReason; - public class SNDCPDeactivateTDSContext extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPUserData.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java similarity index 98% rename from src/decode/p25/message/pdu/confirmed/SNDCPUserData.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java index bb242dda..072e5a24 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPUserData.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.IPHeaderCompression; +import module.decode.p25.reference.IPProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.IPHeaderCompression; -import decode.p25.reference.IPProtocol; - public class SNDCPUserData extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java rename to src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java index a685a213..e4d2a5a4 100644 --- a/src/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java @@ -1,13 +1,13 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class AdjacentStatusBroadcastExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/CallAlertExtended.java b/src/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java similarity index 91% rename from src/decode/p25/message/pdu/osp/control/CallAlertExtended.java rename to src/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java index eeb84130..ef23c505 100644 --- a/src/decode/p25/message/pdu/osp/control/CallAlertExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class CallAlertExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java rename to src/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java index 0725a28a..0cacd7c2 100644 --- a/src/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class GroupAffiliationQueryExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java similarity index 94% rename from src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java rename to src/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java index 06d312a1..03daa837 100644 --- a/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; import edac.CRCP25; public class GroupAffiliationResponseExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java similarity index 93% rename from src/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java index f73213fa..cb3bd496 100644 --- a/src/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class MessageUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java similarity index 90% rename from src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java rename to src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java index 730666de..f9f812a5 100644 --- a/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.P25NetworkCallsign; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.P25NetworkCallsign; import edac.CRCP25; public class NetworkStatusBroadcastExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java b/src/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java similarity index 93% rename from src/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java rename to src/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java index b99858a9..a983bbf0 100644 --- a/src/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java +++ b/src/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; -import decode.p25.reference.Opcode; import edac.CRCP25; public class ProtectionParameterBroadcast extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java rename to src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java index 00206dd1..a8b6a03f 100644 --- a/src/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java @@ -1,13 +1,13 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class RFSSStatusBroadcastExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java similarity index 97% rename from src/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java index 32337408..72353452 100644 --- a/src/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.P25MessageFramer; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.P25MessageFramer; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class RoamingAddressUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/StatusQueryExtended.java b/src/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/StatusQueryExtended.java rename to src/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java index 9ff27ed4..decb3a26 100644 --- a/src/decode/p25/message/pdu/osp/control/StatusQueryExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class StatusQueryExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java similarity index 93% rename from src/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java index 9f831af0..04bc1f4f 100644 --- a/src/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class StatusUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java b/src/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java rename to src/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java index faedb1e5..17670d49 100644 --- a/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; import edac.CRCP25; public class UnitRegistrationResponseExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java similarity index 94% rename from src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java index f8dd957a..d6354d45 100644 --- a/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu.osp.data; +package module.decode.p25.message.pdu.osp.data; import java.util.Date; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class GroupDataChannelGrantExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java similarity index 65% rename from src/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java index efeaf5f7..8b641093 100644 --- a/src/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.data; +package module.decode.p25.message.pdu.osp.data; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.UnitToUnitChannelGrantExtended; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.UnitToUnitChannelGrantExtended; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IndividualDataChannelGrantExtended extends UnitToUnitChannelGrantExtended diff --git a/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java similarity index 94% rename from src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java index 7ec29745..7b9105d7 100644 --- a/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java @@ -1,12 +1,12 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class GroupVoiceChannelGrantExplicit extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java similarity index 94% rename from src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java index 7bc32e65..48105b9a 100644 --- a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; import java.util.Date; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java similarity index 78% rename from src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java index 320dfe4f..67f92214 100644 --- a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java @@ -1,9 +1,9 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TelephoneInterconnectChannelGrantUpdateExplicit extends TelephoneInterconnectChannelGrantExplicit diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java similarity index 94% rename from src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java index 39904e82..81ef77ca 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class UnitToUnitAnswerRequestExplicit extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java similarity index 65% rename from src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java index b065f085..f9542589 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.UnitToUnitChannelGrantExtended; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.UnitToUnitChannelGrantExtended; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitVoiceChannelGrantExtended extends UnitToUnitChannelGrantExtended diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java similarity index 95% rename from src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java index 4c3ac589..a6b3ff06 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java @@ -1,12 +1,12 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/tdu/TDUMessage.java b/src/module/decode/p25/message/tdu/TDUMessage.java similarity index 76% rename from src/decode/p25/message/tdu/TDUMessage.java rename to src/module/decode/p25/message/tdu/TDUMessage.java index 441baee7..0b41292f 100644 --- a/src/decode/p25/message/tdu/TDUMessage.java +++ b/src/module/decode/p25/message/tdu/TDUMessage.java @@ -1,9 +1,9 @@ -package decode.p25.message.tdu; +package module.decode.p25.message.tdu; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.reference.DataUnitID; import edac.CRC; public class TDUMessage extends P25Message diff --git a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java rename to src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java index ae47e968..2cc67bc7 100644 --- a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class AdjacentSiteStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java similarity index 93% rename from src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java index 7e3ff089..d0863292 100644 --- a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class AdjacentSiteStatusBroadcastExplicit extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/CallAlert.java b/src/module/decode/p25/message/tdu/lc/CallAlert.java similarity index 92% rename from src/decode/p25/message/tdu/lc/CallAlert.java rename to src/module/decode/p25/message/tdu/lc/CallAlert.java index 4980933b..6f746519 100644 --- a/src/decode/p25/message/tdu/lc/CallAlert.java +++ b/src/module/decode/p25/message/tdu/lc/CallAlert.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class CallAlert extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/CallTermination.java b/src/module/decode/p25/message/tdu/lc/CallTermination.java similarity index 90% rename from src/decode/p25/message/tdu/lc/CallTermination.java rename to src/module/decode/p25/message/tdu/lc/CallTermination.java index 81e5d699..2a2baeeb 100644 --- a/src/decode/p25/message/tdu/lc/CallTermination.java +++ b/src/module/decode/p25/message/tdu/lc/CallTermination.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class CallTermination extends TDULinkControlMessage { private final static Logger mLog = LoggerFactory.getLogger( CallTermination.class ); diff --git a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java similarity index 93% rename from src/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java rename to src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java index db607906..2730cec4 100644 --- a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdate extends TDULinkControlMessage implements IBandIdentifier { diff --git a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java similarity index 93% rename from src/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java rename to src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java index 3aa1a959..b70210d5 100644 --- a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdateExplicit extends TDULinkControlMessage implements IBandIdentifier { diff --git a/src/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java b/src/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java similarity index 93% rename from src/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java rename to src/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java index 7e9e6061..55226ec6 100644 --- a/src/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java +++ b/src/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.ExtendedFunction; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.ExtendedFunction; -import decode.p25.reference.LinkControlOpcode; - public class ExtendedFunctionCommand extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/GroupAffiliationQuery.java b/src/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java similarity index 92% rename from src/decode/p25/message/tdu/lc/GroupAffiliationQuery.java rename to src/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java index ef8e56b5..9a28ea64 100644 --- a/src/decode/p25/message/tdu/lc/GroupAffiliationQuery.java +++ b/src/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class GroupAffiliationQuery extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java similarity index 95% rename from src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java rename to src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java index 579d0db5..8f9521ef 100644 --- a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java @@ -1,15 +1,15 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; import java.util.Set; import java.util.TreeSet; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.LinkControlOpcode; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdate extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java similarity index 92% rename from src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java rename to src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java index a934e37f..16a0211b 100644 --- a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdateExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java similarity index 95% rename from src/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java rename to src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java index b4268dc8..3db30f4e 100644 --- a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUser extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/MessageUpdate.java b/src/module/decode/p25/message/tdu/lc/MessageUpdate.java similarity index 93% rename from src/decode/p25/message/tdu/lc/MessageUpdate.java rename to src/module/decode/p25/message/tdu/lc/MessageUpdate.java index 6e719b2a..d0130219 100644 --- a/src/decode/p25/message/tdu/lc/MessageUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/MessageUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class MessageUpdate extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java rename to src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java index a240a49b..b298fad6 100644 --- a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class NetworkStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java similarity index 92% rename from src/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java index db36edc5..ba136500 100644 --- a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class NetworkStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java b/src/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java similarity index 91% rename from src/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java rename to src/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java index 3d5a98d6..61b58083 100644 --- a/src/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.Encryption; -import decode.p25.reference.LinkControlOpcode; - public class ProtectionParameterBroadcast extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java similarity index 91% rename from src/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java rename to src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java index d67fbb7b..7864b3c8 100644 --- a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java similarity index 93% rename from src/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java index acfca7cf..452b3d51 100644 --- a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java similarity index 93% rename from src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java rename to src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java index d454c2e3..6a3587df 100644 --- a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java similarity index 92% rename from src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java index 6607c7e2..fb35f425 100644 --- a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/StatusQuery.java b/src/module/decode/p25/message/tdu/lc/StatusQuery.java similarity index 92% rename from src/decode/p25/message/tdu/lc/StatusQuery.java rename to src/module/decode/p25/message/tdu/lc/StatusQuery.java index c0e4da8a..22ffeb3e 100644 --- a/src/decode/p25/message/tdu/lc/StatusQuery.java +++ b/src/module/decode/p25/message/tdu/lc/StatusQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class StatusQuery extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/StatusUpdate.java b/src/module/decode/p25/message/tdu/lc/StatusUpdate.java similarity index 94% rename from src/decode/p25/message/tdu/lc/StatusUpdate.java rename to src/module/decode/p25/message/tdu/lc/StatusUpdate.java index 949687da..2f99d620 100644 --- a/src/decode/p25/message/tdu/lc/StatusUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/StatusUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class StatusUpdate extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/SystemServiceBroadcast.java b/src/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java similarity index 91% rename from src/decode/p25/message/tdu/lc/SystemServiceBroadcast.java rename to src/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java index 4a13d385..70188f08 100644 --- a/src/decode/p25/message/tdu/lc/SystemServiceBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; import java.util.List; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class SystemServiceBroadcast extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/TDULCMessageFactory.java b/src/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java similarity index 97% rename from src/decode/p25/message/tdu/lc/TDULCMessageFactory.java rename to src/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java index 230348c2..67801d93 100644 --- a/src/decode/p25/message/tdu/lc/TDULCMessageFactory.java +++ b/src/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.Vendor; - public class TDULCMessageFactory { diff --git a/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java b/src/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java similarity index 96% rename from src/decode/p25/message/tdu/lc/TDULinkControlMessage.java rename to src/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java index a33424f6..35d8b979 100644 --- a/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java +++ b/src/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java @@ -1,16 +1,17 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.P25Message; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Vendor; +import module.decode.p25.reference.VendorLinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Vendor; -import decode.p25.reference.VendorLinkControlOpcode; import edac.CRC; import edac.Golay24; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java similarity index 94% rename from src/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java rename to src/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java index 1efc2d83..fd8f4c9f 100644 --- a/src/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java +++ b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java @@ -1,14 +1,14 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; import java.util.ArrayList; import java.util.List; +import module.decode.p25.reference.Digit; +import module.decode.p25.reference.LinkControlOpcode; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.Digit; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectAnswerRequest extends TDULinkControlMessage { private final static Logger mLog = LoggerFactory.getLogger( TelephoneInterconnectAnswerRequest.class ); diff --git a/src/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java similarity index 95% rename from src/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java rename to src/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java index 6589339c..d10b347e 100644 --- a/src/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java +++ b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectVoiceChannelUser extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java b/src/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java similarity index 94% rename from src/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java rename to src/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java index 96af11b2..7cdc9d59 100644 --- a/src/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java +++ b/src/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitAuthenticationCommand extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitRegistrationCommand.java b/src/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java similarity index 94% rename from src/decode/p25/message/tdu/lc/UnitRegistrationCommand.java rename to src/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java index 6b8e4e2c..d66360d6 100644 --- a/src/decode/p25/message/tdu/lc/UnitRegistrationCommand.java +++ b/src/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitRegistrationCommand extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java b/src/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java similarity index 95% rename from src/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java rename to src/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java index 880bccea..90bacc50 100644 --- a/src/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java +++ b/src/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitAnswerRequest extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java b/src/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java similarity index 95% rename from src/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java rename to src/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java index b1430222..47157a58 100644 --- a/src/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java +++ b/src/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitVoiceChannelUser extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tsbk/ChannelGrant.java b/src/module/decode/p25/message/tsbk/ChannelGrant.java similarity index 85% rename from src/decode/p25/message/tsbk/ChannelGrant.java rename to src/module/decode/p25/message/tsbk/ChannelGrant.java index f419a4cd..b0c81548 100644 --- a/src/decode/p25/message/tsbk/ChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/ChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public abstract class ChannelGrant extends ServiceMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/GroupChannelGrant.java b/src/module/decode/p25/message/tsbk/GroupChannelGrant.java similarity index 92% rename from src/decode/p25/message/tsbk/GroupChannelGrant.java rename to src/module/decode/p25/message/tsbk/GroupChannelGrant.java index e224dea2..446f3631 100644 --- a/src/decode/p25/message/tsbk/GroupChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/GroupChannelGrant.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class GroupChannelGrant extends ChannelGrant { diff --git a/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java b/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java similarity index 92% rename from src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java rename to src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java index 7bf8d4a7..5233162f 100644 --- a/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java +++ b/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.reference.DataUnitID; public abstract class GroupChannelGrantExplicit extends ChannelGrant { diff --git a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java b/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java similarity index 91% rename from src/decode/p25/message/tsbk/GroupMultiChannelGrant.java rename to src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java index 41262669..38a64d8f 100644 --- a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public abstract class GroupMultiChannelGrant extends ChannelGrant implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/ServiceMessage.java b/src/module/decode/p25/message/tsbk/ServiceMessage.java similarity index 88% rename from src/decode/p25/message/tsbk/ServiceMessage.java rename to src/module/decode/p25/message/tsbk/ServiceMessage.java index 88548fca..78d200ca 100644 --- a/src/decode/p25/message/tsbk/ServiceMessage.java +++ b/src/module/decode/p25/message/tsbk/ServiceMessage.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class ServiceMessage extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/TSBKMessage.java b/src/module/decode/p25/message/tsbk/TSBKMessage.java similarity index 91% rename from src/decode/p25/message/tsbk/TSBKMessage.java rename to src/module/decode/p25/message/tsbk/TSBKMessage.java index 6aa79d87..cbbec09e 100644 --- a/src/decode/p25/message/tsbk/TSBKMessage.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessage.java @@ -1,13 +1,13 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.message.tsbk.vendor.VendorOpcode; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.message.tsbk.vendor.VendorOpcode; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Vendor; import edac.CRC; public class TSBKMessage extends P25Message diff --git a/src/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java similarity index 67% rename from src/decode/p25/message/tsbk/TSBKMessageFactory.java rename to src/module/decode/p25/message/tsbk/TSBKMessageFactory.java index dae0b96b..872e11ef 100644 --- a/src/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -1,62 +1,62 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; +import module.decode.p25.message.tsbk.motorola.PatchGroupAdd; +import module.decode.p25.message.tsbk.motorola.PatchGroupDelete; +import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.motorola.PlannedControlChannnelShutdown; +import module.decode.p25.message.tsbk.motorola.SystemLoading; +import module.decode.p25.message.tsbk.motorola.TrafficChannelBaseStationIdentification; +import module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; +import module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; +import module.decode.p25.message.tsbk.osp.control.CallAlert; +import module.decode.p25.message.tsbk.osp.control.DenyResponse; +import module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF; +import module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; +import module.decode.p25.message.tsbk.osp.control.MessageUpdate; +import module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; +import module.decode.p25.message.tsbk.osp.control.QueuedResponse; +import module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; +import module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; +import module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; +import module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit; +import module.decode.p25.message.tsbk.osp.control.StatusQuery; +import module.decode.p25.message.tsbk.osp.control.StatusUpdate; +import module.decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; +import module.decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement; +import module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; -import decode.p25.message.tsbk.motorola.PatchGroupAdd; -import decode.p25.message.tsbk.motorola.PatchGroupDelete; -import decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; -import decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.motorola.PlannedControlChannnelShutdown; -import decode.p25.message.tsbk.motorola.SystemLoading; -import decode.p25.message.tsbk.motorola.TrafficChannelBaseStationIdentification; -import decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import decode.p25.message.tsbk.osp.control.CallAlert; -import decode.p25.message.tsbk.osp.control.DenyResponse; -import decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; -import decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; -import decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF; -import decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF; -import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import decode.p25.message.tsbk.osp.control.MessageUpdate; -import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; -import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import decode.p25.message.tsbk.osp.control.QueuedResponse; -import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; -import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit; -import decode.p25.message.tsbk.osp.control.StatusQuery; -import decode.p25.message.tsbk.osp.control.StatusUpdate; -import decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; -import decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement; -import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; -import decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Vendor; public class TSBKMessageFactory { diff --git a/src/decode/p25/message/tsbk/UnitChannelGrant.java b/src/module/decode/p25/message/tsbk/UnitChannelGrant.java similarity index 91% rename from src/decode/p25/message/tsbk/UnitChannelGrant.java rename to src/module/decode/p25/message/tsbk/UnitChannelGrant.java index 678fe0b3..5d49f4b7 100644 --- a/src/decode/p25/message/tsbk/UnitChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/UnitChannelGrant.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class UnitChannelGrant extends ChannelGrant { diff --git a/src/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java b/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java similarity index 90% rename from src/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java rename to src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java index f441f87f..987d84ad 100644 --- a/src/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java +++ b/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class ControlChannelBaseStationIdentification extends MotorolaTSBKMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java b/src/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java similarity index 98% rename from src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java rename to src/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java index 848578bb..d610874d 100644 --- a/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java +++ b/src/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; public enum MotorolaOpcode { diff --git a/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java b/src/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java similarity index 84% rename from src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java rename to src/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java index 9f5b3372..09cbaa4c 100644 --- a/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java +++ b/src/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; public class MotorolaTSBKMessage extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroup.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroup.java similarity index 94% rename from src/decode/p25/message/tsbk/motorola/PatchGroup.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroup.java index 594635f8..f5c5305a 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroup.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroup.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class PatchGroup extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupAdd.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java similarity index 79% rename from src/decode/p25/message/tsbk/motorola/PatchGroupAdd.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java index 737b8677..53e8504c 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupAdd.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class PatchGroupAdd extends PatchGroup { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupDelete.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java similarity index 79% rename from src/decode/p25/message/tsbk/motorola/PatchGroupDelete.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java index 0d9029aa..a8496d55 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupDelete.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class PatchGroupDelete extends PatchGroup { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java similarity index 94% rename from src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java index 4993cb8d..6d7f5e5a 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class PatchGroupVoiceChannelGrant extends MotorolaTSBKMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java similarity index 94% rename from src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java index 612fa27e..9dfa50b2 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class PatchGroupVoiceChannelGrantUpdate extends MotorolaTSBKMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java b/src/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java similarity index 83% rename from src/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java rename to src/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java index ee2236c6..19f57c30 100644 --- a/src/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java +++ b/src/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class PlannedControlChannnelShutdown extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/SystemLoading.java b/src/module/decode/p25/message/tsbk/motorola/SystemLoading.java similarity index 79% rename from src/decode/p25/message/tsbk/motorola/SystemLoading.java rename to src/module/decode/p25/message/tsbk/motorola/SystemLoading.java index fb0c9225..a77df53d 100644 --- a/src/decode/p25/message/tsbk/motorola/SystemLoading.java +++ b/src/module/decode/p25/message/tsbk/motorola/SystemLoading.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; public class SystemLoading extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java b/src/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java similarity index 92% rename from src/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java rename to src/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java index ba83eea6..d55ea401 100644 --- a/src/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java +++ b/src/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class TrafficChannelBaseStationIdentification extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java b/src/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java similarity index 90% rename from src/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java index 7f399f4e..5e0bc56c 100644 --- a/src/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class AcknowledgeResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java index b51d91da..227bbcc3 100644 --- a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java @@ -1,12 +1,12 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class AdjacentStatusBroadcast extends TSBKMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java b/src/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java index 62db6654..8fc0f6b2 100644 --- a/src/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class AuthenticationCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/CallAlert.java b/src/module/decode/p25/message/tsbk/osp/control/CallAlert.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/control/CallAlert.java rename to src/module/decode/p25/message/tsbk/osp/control/CallAlert.java index 50a531e5..ce95f61f 100644 --- a/src/decode/p25/message/tsbk/osp/control/CallAlert.java +++ b/src/module/decode/p25/message/tsbk/osp/control/CallAlert.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class CallAlert extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/DenyResponse.java b/src/module/decode/p25/message/tsbk/osp/control/DenyResponse.java similarity index 87% rename from src/decode/p25/message/tsbk/osp/control/DenyResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/DenyResponse.java index 1981488c..7e3e67fe 100644 --- a/src/decode/p25/message/tsbk/osp/control/DenyResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/DenyResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.DenyReason; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.DenyReason; -import decode.p25.reference.Opcode; public class DenyResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java b/src/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java index f63d2422..92cd17e8 100644 --- a/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.ExtendedFunction; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.ExtendedFunction; -import decode.p25.reference.Opcode; public class ExtendedFunctionCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java similarity index 85% rename from src/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java rename to src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java index 58c4bad1..683ab076 100644 --- a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java +++ b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupAffiliationQuery extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java similarity index 87% rename from src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java index 3c59ef10..2c964a0c 100644 --- a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class GroupAffiliationResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java similarity index 82% rename from src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java index 6df9feb9..1f3e7e5e 100644 --- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; public abstract class IdentifierUpdate extends TSBKMessage implements IBandIdentifier diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java rename to src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java index 60e62f00..e4719edb 100644 --- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IdentifierUpdateNonVUHF extends IdentifierUpdate { diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java similarity index 89% rename from src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java rename to src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java index d6dca040..3dffa8cd 100644 --- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IdentifierUpdateVUHF extends IdentifierUpdate { diff --git a/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java b/src/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java index 2d8b3375..aa84caf6 100644 --- a/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class LocationRegistrationResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/MessageUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/MessageUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java index 4fcbee3b..c3a17372 100644 --- a/src/decode/p25/message/tsbk/osp/control/MessageUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class MessageUpdate extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java index fcce3e48..9acd5b99 100644 --- a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.P25NetworkCallsign; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.P25NetworkCallsign; public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java index aa2d9e81..38134ae3 100644 --- a/src/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; -import decode.p25.reference.Opcode; public class ProtectionParameterUpdate extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java b/src/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/control/QueuedResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java index 8548d0e3..4a7de58b 100644 --- a/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.QueuedResponseReason; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.QueuedResponseReason; public class QueuedResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java index 1939bb93..b1f95c7a 100644 --- a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java @@ -1,12 +1,12 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class RFSSStatusBroadcast extends TSBKMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java b/src/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java index a7e8217d..e4df302e 100644 --- a/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class RadioUnitMonitorCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java b/src/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java similarity index 82% rename from src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java index 27967138..e5869719 100644 --- a/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java @@ -1,12 +1,12 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; +import module.decode.p25.reference.StackOperation; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; -import decode.p25.reference.StackOperation; public class RoamingAddressCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java similarity index 90% rename from src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java index ac4cb6b6..48b61992 100644 --- a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java @@ -1,15 +1,16 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class SecondaryControlChannelBroadcast extends TSBKMessage implements IdentifierReceiver, Comparable diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java similarity index 91% rename from src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java rename to src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java index 9cdb0369..e918901f 100644 --- a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java @@ -1,12 +1,13 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; + +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class SecondaryControlChannelBroadcastExplicit extends SecondaryControlChannelBroadcast diff --git a/src/decode/p25/message/tsbk/osp/control/StatusQuery.java b/src/module/decode/p25/message/tsbk/osp/control/StatusQuery.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/control/StatusQuery.java rename to src/module/decode/p25/message/tsbk/osp/control/StatusQuery.java index 321bf613..8d856dbc 100644 --- a/src/decode/p25/message/tsbk/osp/control/StatusQuery.java +++ b/src/module/decode/p25/message/tsbk/osp/control/StatusQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class StatusQuery extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java similarity index 87% rename from src/decode/p25/message/tsbk/osp/control/StatusUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java index ac4da983..59290a61 100644 --- a/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class StatusUpdate extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/SystemService.java b/src/module/decode/p25/message/tsbk/osp/control/SystemService.java similarity index 93% rename from src/decode/p25/message/tsbk/osp/control/SystemService.java rename to src/module/decode/p25/message/tsbk/osp/control/SystemService.java index b87c0d7e..e296775a 100644 --- a/src/decode/p25/message/tsbk/osp/control/SystemService.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SystemService.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; public class SystemService { diff --git a/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java similarity index 83% rename from src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java index ed3f298f..4ff2b25f 100644 --- a/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; import java.util.List; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Service; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Service; public class SystemServiceBroadcast extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java b/src/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java similarity index 94% rename from src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java rename to src/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java index 08f36752..acd12f83 100644 --- a/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java +++ b/src/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -6,11 +6,11 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TimeAndDateAnnouncement extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java b/src/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java similarity index 83% rename from src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java rename to src/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java index 2bd147fc..483bdea5 100644 --- a/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java +++ b/src/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class UnitDeregistrationAcknowledge extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java similarity index 85% rename from src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java index 28f18beb..b5d4b72a 100644 --- a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitRegistrationCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java similarity index 85% rename from src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java index 9e4809dd..7c85fd9a 100644 --- a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class UnitRegistrationResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java rename to src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java index bf4d0e97..27cbafe3 100644 --- a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.GroupMultiChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupMultiChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupDataChannelAnnouncement extends GroupMultiChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java similarity index 69% rename from src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java rename to src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java index 796a754f..fd4fe1bb 100644 --- a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.GroupChannelGrantExplicit; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrantExplicit; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupDataChannelAnnouncementExplicit extends GroupChannelGrantExplicit { diff --git a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java similarity index 67% rename from src/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java index 62eff3eb..0873f1e9 100644 --- a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.GroupChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupDataChannelGrant extends GroupChannelGrant diff --git a/src/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java index ac42a1a0..905f9a5d 100644 --- a/src/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IndividualDataChannelGrant extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPData.java similarity index 82% rename from src/decode/p25/message/tsbk/osp/data/SNDCPData.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPData.java index 577c8662..e5559def 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPData.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.ServiceMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.ServiceMessage; -import decode.p25.reference.DataUnitID; public abstract class SNDCPData extends ServiceMessage { diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java similarity index 95% rename from src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java index 31a99e01..e7a9f5c1 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java similarity index 94% rename from src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java index 06c451c0..88589309 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java similarity index 92% rename from src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java index 0a53c5c4..3fc0ef04 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class SNDCPDataPageRequest extends SNDCPData { diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java index 1232c291..982d9510 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.GroupChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupVoiceChannelGrant extends GroupChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java index 30a9446d..a202ecd1 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.GroupMultiChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupMultiChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupVoiceChannelGrantUpdate extends GroupMultiChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java similarity index 69% rename from src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java rename to src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java index 0a2b776d..46831755 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.GroupChannelGrantExplicit; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrantExplicit; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupVoiceChannelGrantUpdateExplicit extends GroupChannelGrantExplicit { diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java rename to src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java index 5fe3ee75..c21efcd1 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java @@ -1,14 +1,14 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; import java.util.ArrayList; import java.util.List; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Digit; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Digit; -import decode.p25.reference.Opcode; public class TelephoneInterconnectAnswerRequest extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java index 174232f4..61553d59 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java similarity index 75% rename from src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java index 5459f361..02622b73 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TelephoneInterconnectVoiceChannelGrantUpdate extends TelephoneInterconnectVoiceChannelGrant diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java rename to src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java index b1b8aabf..370ee3d6 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitAnswerRequest extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java similarity index 78% rename from src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java index 8c191d27..71274d20 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdate; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.message.tsbk.osp.control.IdentifierUpdate; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitVoiceChannelGrant extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java similarity index 67% rename from src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java index 0f0e0014..9b88d265 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitVoiceChannelGrantUpdate extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/vendor/VendorOpcode.java b/src/module/decode/p25/message/tsbk/vendor/VendorOpcode.java similarity index 98% rename from src/decode/p25/message/tsbk/vendor/VendorOpcode.java rename to src/module/decode/p25/message/tsbk/vendor/VendorOpcode.java index daddb6b1..0067a098 100644 --- a/src/decode/p25/message/tsbk/vendor/VendorOpcode.java +++ b/src/module/decode/p25/message/tsbk/vendor/VendorOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.vendor; +package module.decode.p25.message.tsbk.vendor; public enum VendorOpcode { diff --git a/src/decode/p25/reference/DataUnitID.java b/src/module/decode/p25/reference/DataUnitID.java similarity index 98% rename from src/decode/p25/reference/DataUnitID.java rename to src/module/decode/p25/reference/DataUnitID.java index 31e8bcd2..64a2f728 100644 --- a/src/decode/p25/reference/DataUnitID.java +++ b/src/module/decode/p25/reference/DataUnitID.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; /** * The Data Unit ID (DUID) is part of the Network ID (NID) field and indicates diff --git a/src/decode/p25/reference/DenyReason.java b/src/module/decode/p25/reference/DenyReason.java similarity index 95% rename from src/decode/p25/reference/DenyReason.java rename to src/module/decode/p25/reference/DenyReason.java index be82407c..1668bf68 100644 --- a/src/decode/p25/reference/DenyReason.java +++ b/src/module/decode/p25/reference/DenyReason.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum DenyReason { diff --git a/src/decode/p25/reference/Digit.java b/src/module/decode/p25/reference/Digit.java similarity index 93% rename from src/decode/p25/reference/Digit.java rename to src/module/decode/p25/reference/Digit.java index 1906923b..88ea0448 100644 --- a/src/decode/p25/reference/Digit.java +++ b/src/module/decode/p25/reference/Digit.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; import java.util.List; diff --git a/src/decode/p25/reference/Encryption.java b/src/module/decode/p25/reference/Encryption.java similarity index 96% rename from src/decode/p25/reference/Encryption.java rename to src/module/decode/p25/reference/Encryption.java index 2606c597..1b9fb032 100644 --- a/src/decode/p25/reference/Encryption.java +++ b/src/module/decode/p25/reference/Encryption.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Encryption { diff --git a/src/decode/p25/reference/ExtendedFunction.java b/src/module/decode/p25/reference/ExtendedFunction.java similarity index 97% rename from src/decode/p25/reference/ExtendedFunction.java rename to src/module/decode/p25/reference/ExtendedFunction.java index 3fe9548a..5a72848f 100644 --- a/src/decode/p25/reference/ExtendedFunction.java +++ b/src/module/decode/p25/reference/ExtendedFunction.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum ExtendedFunction { diff --git a/src/decode/p25/reference/IPHeaderCompression.java b/src/module/decode/p25/reference/IPHeaderCompression.java similarity index 94% rename from src/decode/p25/reference/IPHeaderCompression.java rename to src/module/decode/p25/reference/IPHeaderCompression.java index 9418f678..d275d466 100644 --- a/src/decode/p25/reference/IPHeaderCompression.java +++ b/src/module/decode/p25/reference/IPHeaderCompression.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum IPHeaderCompression { diff --git a/src/decode/p25/reference/IPProtocol.java b/src/module/decode/p25/reference/IPProtocol.java similarity index 99% rename from src/decode/p25/reference/IPProtocol.java rename to src/module/decode/p25/reference/IPProtocol.java index 8a384502..b1884eb8 100644 --- a/src/decode/p25/reference/IPProtocol.java +++ b/src/module/decode/p25/reference/IPProtocol.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum IPProtocol { diff --git a/src/decode/p25/reference/LinkControlOpcode.java b/src/module/decode/p25/reference/LinkControlOpcode.java similarity index 99% rename from src/decode/p25/reference/LinkControlOpcode.java rename to src/module/decode/p25/reference/LinkControlOpcode.java index 00262831..c81474b2 100644 --- a/src/decode/p25/reference/LinkControlOpcode.java +++ b/src/module/decode/p25/reference/LinkControlOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum LinkControlOpcode { diff --git a/src/decode/p25/reference/MDPConfigurationOption.java b/src/module/decode/p25/reference/MDPConfigurationOption.java similarity index 93% rename from src/decode/p25/reference/MDPConfigurationOption.java rename to src/module/decode/p25/reference/MDPConfigurationOption.java index d1356380..979f5b48 100644 --- a/src/decode/p25/reference/MDPConfigurationOption.java +++ b/src/module/decode/p25/reference/MDPConfigurationOption.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum MDPConfigurationOption { diff --git a/src/decode/p25/reference/Opcode.java b/src/module/decode/p25/reference/Opcode.java similarity index 99% rename from src/decode/p25/reference/Opcode.java rename to src/module/decode/p25/reference/Opcode.java index f76c8f21..cd3bc40c 100644 --- a/src/decode/p25/reference/Opcode.java +++ b/src/module/decode/p25/reference/Opcode.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Opcode { diff --git a/src/decode/p25/reference/P25NetworkCallsign.java b/src/module/decode/p25/reference/P25NetworkCallsign.java similarity index 97% rename from src/decode/p25/reference/P25NetworkCallsign.java rename to src/module/decode/p25/reference/P25NetworkCallsign.java index 0f3b7028..c2e80860 100644 --- a/src/decode/p25/reference/P25NetworkCallsign.java +++ b/src/module/decode/p25/reference/P25NetworkCallsign.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public class P25NetworkCallsign { diff --git a/src/decode/p25/reference/PDUFormat.java b/src/module/decode/p25/reference/PDUFormat.java similarity index 96% rename from src/decode/p25/reference/PDUFormat.java rename to src/module/decode/p25/reference/PDUFormat.java index fdec7b7c..e2541b8d 100644 --- a/src/decode/p25/reference/PDUFormat.java +++ b/src/module/decode/p25/reference/PDUFormat.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum PDUFormat { diff --git a/src/decode/p25/reference/PDUType.java b/src/module/decode/p25/reference/PDUType.java similarity index 96% rename from src/decode/p25/reference/PDUType.java rename to src/module/decode/p25/reference/PDUType.java index bb200c55..23f5da1e 100644 --- a/src/decode/p25/reference/PDUType.java +++ b/src/module/decode/p25/reference/PDUType.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum PDUType { diff --git a/src/decode/p25/reference/QueuedResponseReason.java b/src/module/decode/p25/reference/QueuedResponseReason.java similarity index 94% rename from src/decode/p25/reference/QueuedResponseReason.java rename to src/module/decode/p25/reference/QueuedResponseReason.java index 9dec86d6..b5de14dd 100644 --- a/src/decode/p25/reference/QueuedResponseReason.java +++ b/src/module/decode/p25/reference/QueuedResponseReason.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum QueuedResponseReason { diff --git a/src/decode/p25/reference/Response.java b/src/module/decode/p25/reference/Response.java similarity index 85% rename from src/decode/p25/reference/Response.java rename to src/module/decode/p25/reference/Response.java index 4951300f..31c7e521 100644 --- a/src/decode/p25/reference/Response.java +++ b/src/module/decode/p25/reference/Response.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Response { diff --git a/src/decode/p25/reference/SNDCPActivationRejectReason.java b/src/module/decode/p25/reference/SNDCPActivationRejectReason.java similarity index 92% rename from src/decode/p25/reference/SNDCPActivationRejectReason.java rename to src/module/decode/p25/reference/SNDCPActivationRejectReason.java index 904045cb..210c4030 100644 --- a/src/decode/p25/reference/SNDCPActivationRejectReason.java +++ b/src/module/decode/p25/reference/SNDCPActivationRejectReason.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum SNDCPActivationRejectReason { diff --git a/src/decode/p25/reference/Service.java b/src/module/decode/p25/reference/Service.java similarity index 97% rename from src/decode/p25/reference/Service.java rename to src/module/decode/p25/reference/Service.java index 8ca2da92..56995f20 100644 --- a/src/decode/p25/reference/Service.java +++ b/src/module/decode/p25/reference/Service.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; import java.util.ArrayList; import java.util.List; diff --git a/src/decode/p25/reference/ServiceAccessPoint.java b/src/module/decode/p25/reference/ServiceAccessPoint.java similarity index 98% rename from src/decode/p25/reference/ServiceAccessPoint.java rename to src/module/decode/p25/reference/ServiceAccessPoint.java index ac1efa2b..ea1c735d 100644 --- a/src/decode/p25/reference/ServiceAccessPoint.java +++ b/src/module/decode/p25/reference/ServiceAccessPoint.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum ServiceAccessPoint { diff --git a/src/decode/p25/reference/StackOperation.java b/src/module/decode/p25/reference/StackOperation.java similarity index 85% rename from src/decode/p25/reference/StackOperation.java rename to src/module/decode/p25/reference/StackOperation.java index 6d93356c..e6250f33 100644 --- a/src/decode/p25/reference/StackOperation.java +++ b/src/module/decode/p25/reference/StackOperation.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum StackOperation { diff --git a/src/decode/p25/reference/Status.java b/src/module/decode/p25/reference/Status.java similarity index 85% rename from src/decode/p25/reference/Status.java rename to src/module/decode/p25/reference/Status.java index de624579..581bb188 100644 --- a/src/decode/p25/reference/Status.java +++ b/src/module/decode/p25/reference/Status.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Status { diff --git a/src/decode/p25/reference/Vendor.java b/src/module/decode/p25/reference/Vendor.java similarity index 99% rename from src/decode/p25/reference/Vendor.java rename to src/module/decode/p25/reference/Vendor.java index 07b504fa..ea521380 100644 --- a/src/decode/p25/reference/Vendor.java +++ b/src/module/decode/p25/reference/Vendor.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Vendor { diff --git a/src/decode/p25/reference/VendorLinkControlOpcode.java b/src/module/decode/p25/reference/VendorLinkControlOpcode.java similarity index 98% rename from src/decode/p25/reference/VendorLinkControlOpcode.java rename to src/module/decode/p25/reference/VendorLinkControlOpcode.java index e32d2c06..8607d5c8 100644 --- a/src/decode/p25/reference/VendorLinkControlOpcode.java +++ b/src/module/decode/p25/reference/VendorLinkControlOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum VendorLinkControlOpcode { diff --git a/src/decode/config/DecodeConfigPassport.java b/src/module/decode/passport/DecodeConfigPassport.java similarity index 90% rename from src/decode/config/DecodeConfigPassport.java rename to src/module/decode/passport/DecodeConfigPassport.java index f6d8d9ae..4ec50db4 100644 --- a/src/decode/config/DecodeConfigPassport.java +++ b/src/module/decode/passport/DecodeConfigPassport.java @@ -15,9 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.passport; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigPassport extends DecodeConfiguration { diff --git a/src/decode/passport/PassportBand.java b/src/module/decode/passport/PassportBand.java similarity index 98% rename from src/decode/passport/PassportBand.java rename to src/module/decode/passport/PassportBand.java index adfa71a2..0a7613c7 100644 --- a/src/decode/passport/PassportBand.java +++ b/src/module/decode/passport/PassportBand.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; public enum PassportBand diff --git a/src/decode/passport/PassportCallEvent.java b/src/module/decode/passport/PassportCallEvent.java similarity index 97% rename from src/decode/passport/PassportCallEvent.java rename to src/module/decode/passport/PassportCallEvent.java index cc9ce255..c0f5bd63 100644 --- a/src/decode/passport/PassportCallEvent.java +++ b/src/module/decode/passport/PassportCallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class PassportCallEvent extends CallEvent { diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java new file mode 100644 index 00000000..0f76acb1 --- /dev/null +++ b/src/module/decode/passport/PassportDecoder.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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 module.decode.passport; + +import instrument.Instrumentable; +import instrument.tap.Tap; + +import java.util.ArrayList; +import java.util.List; + +import module.decode.Decoder; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import alias.AliasList; +import bits.MessageFramer; +import bits.SyncPattern; +import dsp.fsk.LTRFSKDecoder; + +public class PassportDecoder extends Decoder + implements IRealBufferListener, Instrumentable +{ + public static final int PASSPORT_MESSAGE_LENGTH = 68; + public static final int PASSPORT_SYNC_LENGTH = 9; + + private LTRFSKDecoder mPassportFSKDecoder; + private MessageFramer mPassportMessageFramer; + private PassportMessageProcessor mPassportMessageProcessor; + + private List mAvailableTaps; + + public PassportDecoder( DecodeConfiguration config, + AliasList aliasList ) + { + super( new PassportDecoderState( aliasList ) ); + + mPassportFSKDecoder = new LTRFSKDecoder(); + + mPassportMessageFramer = + new MessageFramer( SyncPattern.PASSPORT.getPattern(), + PASSPORT_MESSAGE_LENGTH ); + + mPassportFSKDecoder.addListener( mPassportMessageFramer ); + + mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); + mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); + mPassportMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public Listener getRealBufferListener() + { + return mPassportFSKDecoder; + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.PASSPORT; + } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.addAll( mPassportFSKDecoder.getTaps() ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + mPassportFSKDecoder.addTap( tap ); + } + + @Override + public void removeTap( Tap tap ) + { + mPassportFSKDecoder.removeTap( tap ); + } +} diff --git a/src/decode/passport/PassportPanel.java b/src/module/decode/passport/PassportDecoderPanel.java similarity index 72% rename from src/decode/passport/PassportPanel.java rename to src/module/decode/passport/PassportDecoderPanel.java index e99bde91..a2b01ec1 100644 --- a/src/decode/passport/PassportPanel.java +++ b/src/module/decode/passport/PassportDecoderPanel.java @@ -15,28 +15,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; import java.awt.EventQueue; import javax.swing.JLabel; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; import settings.SettingsManager; import alias.Alias; import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; -public class PassportPanel extends ChannelStatePanel +public class PassportDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol = new JLabel( "Passport" ); private JLabel mToTalkgroup = new JLabel(); private JLabel mToTalkgroupAlias = new JLabel(); @@ -45,13 +41,9 @@ public class PassportPanel extends ChannelStatePanel private JLabel mFromTalkgroup = new JLabel(); private JLabel mFromTalkgroupAlias = new JLabel(); - private SettingsManager mSettingsManager; - - public PassportPanel( SettingsManager settingsManager, Channel channel ) + public PassportDecoderPanel( SettingsManager settingsManager, PassportDecoder decoder ) { - super( channel ); - - mSettingsManager = settingsManager; + super( settingsManager, decoder ); init(); } @@ -59,24 +51,10 @@ public class PassportPanel extends ChannelStatePanel public void dispose() { super.dispose(); - mSettingsManager = null; } public void init() { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); @@ -95,10 +73,6 @@ public class PassportPanel extends ChannelStatePanel mFromTalkgroupAlias.setFont( mFontDecoder ); mFromTalkgroupAlias.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mToTalkgroup ); add( mToTalkgroupAlias, "wrap" ); @@ -107,6 +81,11 @@ public class PassportPanel extends ChannelStatePanel add( mFromTalkgroup ); add( mFromTalkgroupAlias, "wrap" ); } + + private PassportDecoderState getDecoderState() + { + return (PassportDecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute changedAttribute ) @@ -116,24 +95,10 @@ public class PassportPanel extends ChannelStatePanel @Override public void run() { - PassportChannelState state = (PassportChannelState)getChannel() - .getProcessingChain().getChannelState(); - + PassportDecoderState state = getDecoderState(); switch( changedAttribute ) { - case CHANNEL_STATE: - mStateLabel.setText( state.getState().getDisplayValue() ); - break; - case SOURCE: - mSourceLabel.setText( mChannel.getSourceConfiguration() - .getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; case TO_TALKGROUP: String tg = state.getTalkgroup(); @@ -222,10 +187,6 @@ public class PassportPanel extends ChannelStatePanel case CHANNEL_STATE_LABEL_DECODER: if( mColorLabelDecoder != null ) { - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); @@ -252,18 +213,7 @@ public class PassportPanel extends ChannelStatePanel } } break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mColorLabelDetails != null ) - { - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - } + default: break; } } diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java new file mode 100644 index 00000000..96d97725 --- /dev/null +++ b/src/module/decode/passport/PassportDecoderState.java @@ -0,0 +1,529 @@ +/******************************************************************************* + * 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 module.decode.passport; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasList; + +public class PassportDecoderState extends DecoderState +{ + private final static Logger mLog = + LoggerFactory.getLogger( PassportDecoderState.class ); + + private static final int sINT_NULL_VALUE = -1; + private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); + + 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 String mTalkgroup; + private Alias mTalkgroupAlias; + private String mMobileID; + private Alias mMobileIDAlias; + private int mChannelNumber; + private int mSiteNumber; + private PassportBand mSiteBand; + private HashMap mActiveCalls = new HashMap(); + + public PassportDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + @SuppressWarnings( "incomplete-switch" ) + public void receive( Message message ) + { +// if( message instanceof PassportMessage ) +// { +// PassportMessage passport = ((PassportMessage)message); +// +// if( passport.isValid() ) +// { +// switch( passport.getMessageType() ) +// { +// case CA_STRT: +// mSiteLCNs.put( passport.getLCN(), +// passport.getLCNFrequencyFormatted() ); +// case CA_ENDD: +// String talkgroup = +// String.valueOf( passport.getTalkgroupID() ); +// +// if( mTalkgroupsFirstHeard.contains( talkgroup ) ) +// { +// mTalkgroups.add( talkgroup ); +// } +// else +// { +// mTalkgroupsFirstHeard.add( talkgroup ); +// } +// break; +// case ID_RDIO: +// String min = passport.getMobileID(); +// +// if( min != null ) +// { +// mMobileIDs.add( min ); +// } +// break; +// case SY_IDLE: +// if( passport.getFree() != 0 ) +// { +// mNeighborLCNs.put( passport.getFree(), +// passport.getFreeFrequencyFormatted() ); +// } +// break; +// default: +// break; +// } +// } +// } +// +// if( message instanceof PassportMessage ) +// { +// mActivitySummary.receive( message ); +// +// PassportMessage passport = (PassportMessage)message; +// +// if( passport.isValid() ) +// { +// switch( passport.getMessageType() ) +// { +// case CA_STRT: +// if( mChannelNumber == 0 ) +// { +// mChannelNumber = passport.getLCN(); +// } +// +// if( passport.getLCN() == mChannelNumber ) +// { +// setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); +// setTalkgroupAlias( passport.getTalkgroupIDAlias() ); +// +// if( mTalkgroupAlias != null ) +// { +// try +// { +// if( mTalkgroupAlias.hasAudioType() ) +// { +// mAudioTypeListener.setAudioType( +// mTalkgroupAlias.getAudioType() ); +// } +// } +// catch( Exception e ) +// { +// mLog.error( "Error setting audio for " +// + "passport call", e ); +// } +// } +// +// PassportCallEvent current = +// getCurrentPassportCallEvent(); +// +// /* 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 && !current.addMessage( passport ) ) +// { +// if( current.isValid() ) +// { +// final PassportCallEvent endEvent = +// getCurrentPassportCallEvent(); +// +// mCallEventModel.setEnd( endEvent ); +// } +// else +// { +// mCallEventModel.remove( current ); +// } +// +// current = null; +// setCurrentCallEvent( null ); +// } +// +// if( current == null ) +// { +// mCurrentCallEvent = new PassportCallEvent +// .Builder( CallEventType.CALL ) +// .aliasList( mAliasList ) +// .channel( String.valueOf( mChannelNumber ) ) +// .frequency( passport.getLCNFrequency() ) +// .to( String.valueOf( passport.getTalkgroupID() ) ) +// .build(); +// +// mCallEventModel.add( mCurrentCallEvent ); +// } +// +// State state = getState(); +// +// if( state == State.IDLE || state == State.CALL ) +// { +// setState( State.CALL ); +// } +// else if( state == State.DATA ) +// { +// setState( 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 ); +// +// PassportCallEvent event = new PassportCallEvent +// .Builder( CallEventType.CALL_DETECT ) +// .aliasList( mAliasList ) +// .channel( String.valueOf( lcn ) ) +// .details( "Site: " + passport.getSite() ) +// .frequency( passport.getLCNFrequency() ) +// .to( tg ) +// .build(); +// +// mCallEventModel.add( event ); +// } +// } +// break; +// case DA_STRT: +////TODO: add channel number checking and log a call event if on another site +// setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); +// setTalkgroupAlias( passport.getTalkgroupIDAlias() ); +// setState( State.DATA ); +// break; +// case CA_ENDD: +// setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); +// setTalkgroupAlias( passport.getTalkgroupIDAlias() ); +// fade( CallEventType.CALL_END ); +// break; +// case ID_RDIO: +// setMobileID( passport.getMobileID() ); +// setMobileIDAlias( passport.getMobileIDAlias() ); +// +// final String mid = passport.getMobileID(); +// +// final PassportCallEvent event = +// getCurrentPassportCallEvent(); +// +// if( event != null && mid != null ) +// { +// mCallEventModel.setFromID( event, mid ); +// } +// +// setState( State.CALL ); +// break; +// case RA_REGI: +// mCallEventModel.add( new PassportCallEvent +// .Builder( CallEventType.REGISTER ) +// .aliasList( mAliasList ) +// .channel( String.valueOf( passport.getLCN() ) ) +// .frequency( passport.getLCNFrequency() ) +// .to( passport.getToID() ) +// .build() ); +////TODO: add logic to recognize the register event and suppress the subsequent +////call messages, but capture the group assignments. +// setState( State.DATA ); +// break; +// case SY_IDLE: +// PassportBand band = passport.getSiteBand(); +// +// if( mSiteBand == null || mSiteBand != band ) +// { +// mSiteBand = band; +// +// mSiteNumber = passport.getSite(); +// +// long frequency = mProcessingChain.getChannel() +// .getTunerChannel().getFrequency(); +// +// mChannelNumber = mSiteBand.getChannel( frequency ); +// +// broadcast( ChangedAttribute.CHANNEL_NUMBER ); +// } +// default: +// break; +// } +// } +// } + } + + 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() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tPassport\n\n" ); + sb.append( "Site Channels\n" ); + + if( mSiteLCNs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + ArrayList channels = new ArrayList( mSiteLCNs.keySet() ); + Collections.sort( channels ); + + for( Integer channel: channels ) + { + sb.append( " " + channel ); + sb.append( "\t" + mSiteLCNs.get( channel ) ); + sb.append( "\n" ); + } + } + + sb.append( "\nNeighbor Channels\n" ); + + if( mNeighborLCNs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + ArrayList channels = new ArrayList( mNeighborLCNs.keySet() ); + Collections.sort( channels ); + + for( Integer channel: channels ) + { + sb.append( " " + channel ); + sb.append( "\t" + mNeighborLCNs.get( channel ) ); + sb.append( "\n" ); + } + } + + sb.append( "\nTalkgroups\n" ); + + if( mTalkgroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + 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( "\nMobile ID Numbers\n" ); + + if( mMobileIDs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + 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" ); + } + } + + return sb.toString(); + } + +// /** +// * Intercept the fade event so that we can generate a call end event +// */ +// @Override +// public void fade( final CallEventType type ) +// { +// /* +// * We can receive multiple call tear-down messages -- only post a call +// * end event for the one that can change the state to fade +// */ +// if( getState().canChangeTo( State.FADE ) ) +// { +// final PassportCallEvent current = getCurrentPassportCallEvent(); +// +// if( current != null ) +// { +// mCallEventModel.setEnd( current ); +// } +// } +// +// setCurrentCallEvent( null ); +// +// mActiveCalls.clear(); +// +// super.fade( type ); +// } + + public void reset() + { + //TODO: finish this + resetState(); + } + + private void resetState() + { + setTalkgroup( null ); + setTalkgroupAlias( null ); + + setMobileID( null ); + setMobileIDAlias( null ); + } + + public int getChannelNumber() + { + return mChannelNumber; + } + + public int getSiteNumber() + { + return mSiteNumber; + } + + public String getTalkgroup() + { + return mTalkgroup; + } + + public void setTalkgroup( String talkgroup ) + { + mTalkgroup = talkgroup; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getTalkgroupAlias() + { + return mTalkgroupAlias; + } + + public void setTalkgroupAlias( Alias alias ) + { + mTalkgroupAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMobileID() + { + return mMobileID; + } + + public void setMobileID( String id ) + { + mMobileID = id; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getMobileIDAlias() + { + return mMobileIDAlias; + } + + public void setMobileIDAlias( Alias alias ) + { + mMobileIDAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + @Override + public void init() + { + // TODO Auto-generated method stub + + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + // TODO Auto-generated method stub + + } +} diff --git a/src/decode/passport/PassportEditor.java b/src/module/decode/passport/PassportEditor.java similarity index 90% rename from src/decode/passport/PassportEditor.java rename to src/module/decode/passport/PassportEditor.java index 26cc814f..4b39e15d 100644 --- a/src/decode/passport/PassportEditor.java +++ b/src/module/decode/passport/PassportEditor.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; -import decode.DecodeEditor; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class PassportEditor extends DecodeEditor { diff --git a/src/decode/passport/PassportMessage.java b/src/module/decode/passport/PassportMessage.java similarity index 99% rename from src/decode/passport/PassportMessage.java rename to src/module/decode/passport/PassportMessage.java index 59d156e1..36b183a5 100644 --- a/src/decode/passport/PassportMessage.java +++ b/src/module/decode/passport/PassportMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; import java.text.DecimalFormat; @@ -26,10 +26,10 @@ import java.util.List; import map.Plottable; import message.Message; import message.MessageType; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.DecoderType; import edac.CRC; import edac.CRCPassport; diff --git a/src/decode/passport/PassportMessageFilter.java b/src/module/decode/passport/PassportMessageFilter.java similarity index 98% rename from src/decode/passport/PassportMessageFilter.java rename to src/module/decode/passport/PassportMessageFilter.java index f4026101..6be05f84 100644 --- a/src/decode/passport/PassportMessageFilter.java +++ b/src/module/decode/passport/PassportMessageFilter.java @@ -1,4 +1,4 @@ -package decode.passport; +package module.decode.passport; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/passport/PassportMessageProcessor.java b/src/module/decode/passport/PassportMessageProcessor.java similarity index 77% rename from src/decode/passport/PassportMessageProcessor.java rename to src/module/decode/passport/PassportMessageProcessor.java index c3031390..553e5a35 100644 --- a/src/decode/passport/PassportMessageProcessor.java +++ b/src/module/decode/passport/PassportMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,20 +15,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; import java.util.HashMap; import message.Message; import message.MessageType; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; public class PassportMessageProcessor implements Listener { - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private IdleMessageFinder mIdleFinder = new IdleMessageFinder(); private AliasList mAliasList; private PassportMessage mIdleMessage; @@ -41,29 +40,32 @@ public class PassportMessageProcessor implements Listener @Override public void receive( BinaryMessage buffer ) { - PassportMessage message; - - if( mIdleMessage != null ) + if( mMessageListener != null ) { - message = new PassportMessage( buffer, mIdleMessage, mAliasList ); - } - else - { - message = new PassportMessage( buffer, mAliasList ); - mIdleFinder.receive( message ); - } + PassportMessage message; + + if( mIdleMessage != null ) + { + message = new PassportMessage( buffer, mIdleMessage, mAliasList ); + } + else + { + message = new PassportMessage( buffer, mAliasList ); + mIdleFinder.receive( message ); + } - mBroadcaster.receive( message ); + mMessageListener.receive( message ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } public class IdleMessageFinder diff --git a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java new file mode 100644 index 00000000..c147a1e8 --- /dev/null +++ b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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 module.decode.state; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import message.Message; +import module.ProcessingChain; +import module.decode.state.DecoderStateEvent.Event; + +/** + * Basic decoder channel state - provides the minimum channel state functionality + * to support an always un-squelched audio decoder. + */ +public class AlwaysUnsquelchedDecoderState extends DecoderState +{ + private final static Logger mLog = + LoggerFactory.getLogger( AlwaysUnsquelchedDecoderState.class ); + + private String mProtocol; + + public AlwaysUnsquelchedDecoderState( String protocol ) + { + super( null ); + + mProtocol = protocol; + } + + @Override + public void init() + { + mLog.debug( "init() - broadcasting unsquelch event" ); + + broadcast( new DecoderStateEvent( this, Event.ALWAYS_UNSQUELCH, State.IDLE ) ); + } + + @Override + public void reset() + { + /* No reset actions needed */ + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "\tDecoder:\t" ); + sb.append( mProtocol ); + sb.append( "\n\n" ); + + return sb.toString(); + } + + @Override + public void receive( Message t ) + { + /* Not implemented */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + /* Not implemented */ + } +} diff --git a/src/module/decode/state/ChangedAttribute.java b/src/module/decode/state/ChangedAttribute.java new file mode 100644 index 00000000..34a2881b --- /dev/null +++ b/src/module/decode/state/ChangedAttribute.java @@ -0,0 +1,29 @@ +package module.decode.state; + +public enum ChangedAttribute +{ + AUDIO_TYPE, + CHANNEL_NUMBER, + CHANNEL_NAME, + CHANNEL_SITE_NUMBER, + CHANNEL_SITE_NUMBER_ALIAS, + CHANNEL_STATE, + DESCRIPTION, + FROM_TALKGROUP, + FROM_TALKGROUP_ALIAS, + FROM_TALKGROUP_TYPE, + MESSAGE, + MESSAGE_TYPE, + NAC, + SITE, + SITE_ALIAS, + SITE_NAME, + SOURCE, + SYSTEM, + SYSTEM_NAME, + SQUELCH, + TO_TALKGROUP, + TO_TALKGROUP_ALIAS, + TO_TALKGROUP_TYPE, + WACN; +} \ No newline at end of file diff --git a/src/module/decode/state/ChannelCollectionPanel.java b/src/module/decode/state/ChannelCollectionPanel.java new file mode 100644 index 00000000..e7d74e0b --- /dev/null +++ b/src/module/decode/state/ChannelCollectionPanel.java @@ -0,0 +1,150 @@ +package module.decode.state; + +import java.awt.Component; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JMenu; +import javax.swing.JPanel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; +import settings.SettingsManager; +import controller.channel.Channel; + +/** + * Channel Collection assembles a primary channel panel and any related traffic + * channel panels, provides contextual access to the primary channel and + * supports channel selection for the primary or any traffic channels. + */ +public class ChannelCollectionPanel extends JPanel +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( ChannelCollectionPanel.class ); + + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; + + private List mChannelPanels = new ArrayList(); + + public ChannelCollectionPanel( PlaylistManager playlistManager, + SettingsManager settingsManager, + Channel channel ) + { + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; + + init( channel ); + } + + protected void init( Channel channel ) + { + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + setOpaque( false ); + + ChannelStatePanel csp = new ChannelStatePanel( mPlaylistManager, + mSettingsManager, channel ); + + mChannelPanels.add( csp ); + + add( csp, "span,grow" ); + + } + + public Channel getChannel() + { + if( mChannelPanels.size() >= 1 ) + { + return mChannelPanels.get( 0 ).getChannel(); + } + + return null; + } + + public void dispose() + { + for( ChannelStatePanel panel: mChannelPanels ) + { + panel.dispose(); + } + + mChannelPanels.clear(); + + mPlaylistManager = null; + mSettingsManager = null; + } + + /** + * Toggles the selection state of the channel if it matches the channel + * argument. Otherwise, sets the selection state to false. + */ + public void setSelectedChannel( Channel channel ) + { + for( ChannelStatePanel panel: mChannelPanels ) + { + if( panel.getChannel() == channel ) + { + /* Toggle the selection state */ + panel.getChannel().setSelected( !panel.getChannel().isSelected() ); + panel.repaint(); + } + else + { + if( panel.getChannel().isSelected() ) + { + panel.getChannel().setSelected( false ); + panel.repaint(); + } + } + } + } + + /** + * Returns the channel represented by the channel panel at the gui point + * location, to selecting the channel + * + * @param point - current location of the mouse + * + * @return - channel or null + */ + public Channel getChannelAt( Point point ) + { + Component c = this.getComponentAt( point ); + + if( c instanceof ChannelCollectionPanel ) + { + return ((ChannelCollectionPanel)c).getChannel(); + } + else + { + return getChannel(); + } + } + + protected SettingsManager getSettingsManager() + { + return mSettingsManager; + } + + + public JMenu getContextMenu() + { + if( !mChannelPanels.isEmpty() ) + { + return mChannelPanels.get( 0 ).getContextMenu(); + } + + return null; + } + + public void addTrafficChannel( Channel channel ) + { + //TODO: add logic to handle traffic channels. + } +} diff --git a/src/controller/state/ChannelStateList.java b/src/module/decode/state/ChannelList.java similarity index 56% rename from src/controller/state/ChannelStateList.java rename to src/module/decode/state/ChannelList.java index bcd1aa72..920fe1e0 100644 --- a/src/controller/state/ChannelStateList.java +++ b/src/module/decode/state/ChannelList.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.state; +package module.decode.state; import java.awt.Color; import java.awt.Component; @@ -30,47 +30,38 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.ColorSettingResetMenuItem; import settings.SettingsManager; import controller.channel.Channel; -import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; -import decode.am.AMChannelState; -import decode.am.AMPanel; -import decode.fleetsync2.FleetsyncChannelState; -import decode.fleetsync2.FleetsyncPanel; -import decode.lj1200.LJ1200ChannelState; -import decode.lj1200.LJ1200Panel; -import decode.ltrnet.LTRNetChannelState; -import decode.ltrnet.LTRNetPanel; -import decode.ltrstandard.LTRChannelState; -import decode.ltrstandard.LTRPanel; -import decode.mdc1200.MDCChannelState; -import decode.mdc1200.MDCPanel; -import decode.mpt1327.MPT1327ChannelState; -import decode.mpt1327.MPT1327Panel; -import decode.nbfm.NBFMChannelState; -import decode.nbfm.NBFMPanel; -import decode.p25.P25ChannelState; -import decode.p25.P25Panel; -import decode.passport.PassportChannelState; -import decode.passport.PassportPanel; -public class ChannelStateList extends JPanel implements ChannelEventListener +/** + * Gui wrapper for the list of currently processing primary channels + */ +public class ChannelList extends JPanel implements ChannelEventListener { private static final long serialVersionUID = 1L; - - private HashMap mDisplayedPanels = - new HashMap(); - + + private final static Logger mLog = LoggerFactory.getLogger( ChannelList.class ); + + private HashMap mDisplayedPanels = + new HashMap(); + + private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; - public ChannelStateList( SettingsManager settingsManager ) + public ChannelList( PlaylistManager playlistManager, + SettingsManager settingsManager ) { + mPlaylistManager = playlistManager; mSettingsManager = settingsManager; init(); @@ -87,205 +78,60 @@ public class ChannelStateList extends JPanel implements ChannelEventListener @Override public void channelChanged( ChannelEvent event ) { - ChannelType type = event.getChannel().getChannelType(); - switch( event.getEvent() ) { - case PROCESSING_STARTED: - if( type == ChannelType.STANDARD ) - { - channelStarted( event.getChannel() ); - } - else if( type == ChannelType.TRAFFIC ) - { - for( Channel parent: mDisplayedPanels.keySet() ) - { - if( parent.hasTrafficChannel( event.getChannel() ) ) - { - mDisplayedPanels.get( parent ) - .trafficChannelAdded( event.getChannel() ); - validate(); - } - } - } + case CHANNEL_PROCESSING_STARTED: + addChannelPanel( event.getChannel() ); break; - case PROCESSING_STOPPED: - if( type == ChannelType.STANDARD ) - { - channelStopped( event.getChannel() ); - } - /* Since we don't have a reference to the parent channel that - * owns the traffic channel, iterate all channels to find the - * parent */ - else if( type == ChannelType.TRAFFIC ) - { - for( Channel control: mDisplayedPanels.keySet() ) - { - if( control.hasTrafficChannel( event.getChannel() ) ) - { - control.removeTrafficChannel( event.getChannel() ); - - mDisplayedPanels.get( control ) - .trafficChannelDeleted( event.getChannel() ); - - validate(); - } - } - } + case CHANNEL_PROCESSING_STOPPED: + removeChannelPanel( event.getChannel() ); break; default: break; } } - public void channelStarted( Channel channel ) + private void addChannelPanel( Channel channel ) { if( !mDisplayedPanels.containsKey( channel ) ) { - ChannelState state = channel.getProcessingChain().getChannelState(); - - ChannelStatePanel panel = null; - - if( state instanceof AMChannelState ) - { - panel = new AMPanel( channel ); - } - else if( state instanceof NBFMChannelState ) - { - NBFMChannelState convState = - (NBFMChannelState)state; - - panel = new NBFMPanel( channel ); - - for( AuxChannelState aux: convState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = - new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - else if( aux instanceof MDCChannelState ) - { - MDCPanel mdsPanel = new MDCPanel( mSettingsManager, - (MDCChannelState)aux ); - - panel.addPanel( mdsPanel ); - } - else if( aux instanceof LJ1200ChannelState ) - { - LJ1200Panel ljPanel = new LJ1200Panel( mSettingsManager, - (LJ1200ChannelState)aux ); - - panel.addPanel( ljPanel ); - } - } - } - else if( state instanceof LTRChannelState ) - { - LTRChannelState ltrState = (LTRChannelState)state; - - panel = new LTRPanel( mSettingsManager, channel ); - - for( AuxChannelState aux: ltrState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - } - } - else if( state instanceof LTRNetChannelState ) - { - LTRNetChannelState ltrState = (LTRNetChannelState)state; - - panel = new LTRNetPanel( channel ); - - for( AuxChannelState aux: ltrState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - } - } - else if( state instanceof MPT1327ChannelState ) - { - MPT1327ChannelState mptState = (MPT1327ChannelState)state; - - panel = new MPT1327Panel( channel ); - } - else if( state instanceof PassportChannelState ) - { - PassportChannelState passportState = (PassportChannelState)state; - - panel = new PassportPanel( mSettingsManager, channel ); - - for( AuxChannelState aux: passportState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - } - } - else if( state instanceof P25ChannelState ) - { - panel = new P25Panel( channel ); - } + ChannelCollectionPanel panel = new ChannelCollectionPanel( + mPlaylistManager, mSettingsManager, channel ); - if( panel != null ) - { - add( panel, "wrap" ); - - mDisplayedPanels.put( channel, panel ); - - validate(); - } + add( panel, "wrap" ); + + mDisplayedPanels.put( channel, panel ); + + revalidate(); + repaint(); } } - public void channelStopped( Channel channel ) + public void removeChannelPanel( Channel channel ) { if( mDisplayedPanels.containsKey( channel ) ) { - ChannelStatePanel panel = mDisplayedPanels.get( channel ); + ChannelCollectionPanel panel = mDisplayedPanels.remove( channel ); if( panel != null ) { - remove( panel ); + remove( panel ); - validate(); + panel.dispose(); } - mDisplayedPanels.remove( channel ); + revalidate(); + repaint(); } } - public void setSelectedChannel( ChannelStatePanel selectedPanel ) + public void setSelectedChannel( Channel channel ) { - //De-select any other selected panel - for( ChannelStatePanel panel: mDisplayedPanels.values() ) + /* Send channel selection to each channel panel */ + for( ChannelCollectionPanel panel: mDisplayedPanels.values() ) { - if( panel != selectedPanel && panel.isSelected() ) - { - panel.setSelected( false ); - } + panel.setSelectedChannel( channel ); } - - //Toggle selection state - selectedPanel.setSelected( !selectedPanel.isSelected() ); } public class ListSelectionListener implements MouseListener @@ -295,38 +141,29 @@ public class ChannelStateList extends JPanel implements ChannelEventListener { Component component = getComponentAt( e.getPoint() ); - if( component instanceof ChannelStatePanel ) + if( component instanceof ChannelCollectionPanel ) { - ChannelStatePanel panel = (ChannelStatePanel)component; + ChannelCollectionPanel panel = (ChannelCollectionPanel)component; - /* If the panel has child traffic channels, attempt to get - * one of them for the mouse event */ - if( panel.getChannel().getTrafficChannels().size() > 0 ) - { - for( ChannelStatePanel trafficPanel: panel.getTrafficPanels().values() ) - { - Point translatedPoint = SwingUtilities - .convertPoint( e.getComponent(), e.getPoint(), - trafficPanel ); - - if( trafficPanel.contains( translatedPoint ) ) - { - panel = trafficPanel; - break; - } - } - } + Point translatedPoint = SwingUtilities + .convertPoint( e.getComponent(), e.getPoint(), panel ); + Channel channel = panel.getChannelAt( translatedPoint ); + if( e.getButton() == MouseEvent.BUTTON1 ) { - setSelectedChannel( panel ); + setSelectedChannel( channel ); } else if( e.getButton() == MouseEvent.BUTTON3 ) { JPopupMenu popupMenu = new JPopupMenu(); - JMenu menu = panel.getChannel().getContextMenu(); - - popupMenu.add( menu ); + + JMenu menu = panel.getContextMenu(); + + if( menu != null ) + { + popupMenu.add( menu ); + } JMenu colorMenu = new JMenu( "Color" ); popupMenu.add( colorMenu ); @@ -454,7 +291,7 @@ public class ChannelStateList extends JPanel implements ChannelEventListener selectedMenu.add( new ColorSettingResetMenuItem( mSettingsManager, ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ) ); - popupMenu.show( ChannelStateList.this, e.getX(), e.getY() ); + popupMenu.show( ChannelList.this, e.getX(), e.getY() ); } } } diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java new file mode 100644 index 00000000..546d2e60 --- /dev/null +++ b/src/module/decode/state/ChannelState.java @@ -0,0 +1,587 @@ +/******************************************************************************* + * 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 module.decode.state; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent.Event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import alias.Metadata; +import alias.MetadataReset; +import alias.MetadataType; +import audio.metadata.IMetadataProvider; +import audio.squelch.ISquelchStateProvider; +import audio.squelch.SquelchState; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; +import controller.channel.Channel.ChannelType; + +/** + * Channel state tracks the overall state of all processing modules and decoders + * configured for the channel and provides squelch control events. + * + * Primary channel state provides a state machine for tracking decode events + * during a voice or data call through it's life cycle of CALL/DATA, FADE, END, + * and then back to an IDLE state. This class also provides squelch control + * to support any audio processing. + * + * State Descriptions: + * IDLE No voice or data call activity + * CALL/DATA A voice or data call has started, or is continuing + * CONTROL Control channel + * 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 + * END The phase after the voice or data call FADE, when the call is + * now over, and the gui should be reset to IDLE. + * + * When a call is started, invoke mTimerService.startFadeTimer() to start the + * call fade monitoring timer process that will automatically signal a call end + * once no more call signalling has been detected after the fade timeout has + * occurred, by invoking the fade() method. + * + * When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke + * the mTimerService.startResetTimer(). Once the reset timeout occurs, + * the reset() method will be called, allowing the channel state to perform + * any reset actions, like clearing status and id labels on any view panels. + */ +public class ChannelState extends Module implements ICallEventProvider, + IChangedAttributeProvider, IDecoderStateEventListener, + IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider +{ + private final static Logger mLog = + LoggerFactory.getLogger( ChannelState.class ); + + public static final long FADE_TIMEOUT_DELAY = 1000; + public static final long RESET_TIMEOUT_DELAY = 2000; + + private State mState = State.IDLE; + private long mTrafficChannelTimeout = + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; + + private Listener mCallEventListener; + private Listener mChangedAttributeListener; + private Listener mDecoderStateListener; + private Listener mMetadataListener; + private Listener mSquelchStateListener; + private DecoderStateEventReceiver mDecoderStateEventReceiver = + new DecoderStateEventReceiver(); + + private boolean mSquelchLocked = false; + private boolean mSelected = false; + + private long mFadeTimeout; + private long mResetTimeout; + + private StateMonitor mStateMonitor = new StateMonitor(); + private AtomicBoolean mMonitoring = new AtomicBoolean(); + private ScheduledFuture mRunningMonitor; + + private ThreadPoolManager mThreadPoolManager; + private ChannelType mChannelType; + + private TrafficChannelStatusListener mTrafficChannelStatusListener; + private CallEvent mTrafficChannelCallEvent; + + public ChannelState( ThreadPoolManager manager, ChannelType channelType ) + { + mThreadPoolManager = manager; + mChannelType = channelType; + } + + @Override + public void init() + { + + } + + public void dispose() + { + stopMonitor(); + + mCallEventListener = null; + mChangedAttributeListener = null; + mDecoderStateListener = null; + mSquelchStateListener = null; + mThreadPoolManager = null; + mStateMonitor = null; + } + + public void setTrafficChannelTimeout( long milliseconds ) + { + mTrafficChannelTimeout = milliseconds; + } + + public void setSelected( boolean selected ) + { + mSelected = selected; + } + + public boolean isSelected() + { + return mSelected; + } + + public State getState() + { + return mState; + } + + private boolean isEndingState() + { + return mState == State.IDLE || + mState == State.FADE || + mState == State.END; + } + + + /** + * Starts the state monitor running when we're not in IDLE state. Calls to + * start an already started monitor are ignored. + */ + private void startMonitor() + { + if( mMonitoring.compareAndSet( false, true ) ) + { + if( mRunningMonitor == null ) + { + mRunningMonitor = mThreadPoolManager.scheduleFixedRate( + ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); + } + else + { + throw new RuntimeException( "Channel state monitor's scheduled " + + "future pointer was not null" ); + } + } + } + + /** + * Stops the state monitor when we're in an IDLE state. Calls to stop an + * already stopped monitor are ignored. + */ + private void stopMonitor() + { + if( mMonitoring.compareAndSet( true, false ) ) + { + if( mRunningMonitor != null ) + { + mThreadPoolManager.cancel( mRunningMonitor ); + } + else + { + throw new RuntimeException( "Channel state monitor's scheduled " + + "future pointer was null when trying to stop the monitor" ); + } + + mRunningMonitor = null; + } + } + + /** + * Updates the fade timeout threshold to the current time plus delay + */ + private void updateFadeTimeout() + { + /* Standard channel uses the FADE_TIMEOUT_DELAY and traffic channels + * use the TRAFFIC_FADE_TIMEOUT_DELAY. Some traffic channels (e.g. MPT) + * don't have signaling on the traffic channel that can be used to + * update the fade timeout, so we set the timeout value larger to ensure + * the channel doesn't fade before the end of the call. */ + long timeout = System.currentTimeMillis() + + ( mChannelType == ChannelType.STANDARD ? FADE_TIMEOUT_DELAY : + mTrafficChannelTimeout ); + + if( timeout > mFadeTimeout ) + { + mFadeTimeout = timeout; + } + } + + /** + * Updates the reset timeout threshold to the current time plus delay + */ + private void updateResetTimeout() + { + mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY; + } + + /** + * Broadcasts the squelch state to the registered listener + */ + protected void broadcast( SquelchState state ) + { + if( mSquelchStateListener != null && !mSquelchLocked ) + { + mSquelchStateListener.receive( state ); + } + } + + /** + * Sets the squelch state listener + */ + @Override + public void setSquelchStateListener( Listener listener ) + { + mSquelchStateListener = listener; + } + + /** + * Removes the squelch state listener + */ + @Override + public void removeSquelchStateListener() + { + mSquelchStateListener = null; + } + + /** + * Sets the channel state to the specified state, or updates the timeout + * values so that the state monitor will not change state. Broadcasts a + * squelch event when the state changes and the audio squelch state should + * change. Also broadcasts changed attribute and decoder state events so + * that external processes can maintain sync with this channel state. + */ + protected void setState( State state ) + { + if( mState == state ) + { + switch( state ) + { + case CALL: + case CONTROL: + case DATA: + case ENCRYPTED: + updateFadeTimeout(); + break; + default: + break; + } + } + else if( mState.canChangeTo( state ) ) + { + switch( state ) + { + case CONTROL: + case DATA: + case ENCRYPTED: + broadcast( SquelchState.SQUELCH ); + updateFadeTimeout(); + startMonitor(); + break; + case CALL: + broadcast( SquelchState.UNSQUELCH ); + updateFadeTimeout(); + startMonitor(); + break; + case FADE: + broadcast( SquelchState.SQUELCH ); + updateResetTimeout(); + startMonitor(); + break; + case END: + broadcast( SquelchState.SQUELCH ); + broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); + broadcast( new MetadataReset() ); + break; + case IDLE: + broadcast( SquelchState.SQUELCH ); + stopMonitor(); + + if( mChannelType == ChannelType.TRAFFIC ) + { + if( mTrafficChannelStatusListener != null ) + { + mTrafficChannelStatusListener.callEnd(); + mTrafficChannelStatusListener = null; + } + + if( mTrafficChannelCallEvent != null ) + { + mTrafficChannelCallEvent.end(); + + broadcast( mTrafficChannelCallEvent ); + mTrafficChannelCallEvent = null; + } + } + break; + default: + break; + } + + mState = state; + + broadcast( ChangedAttribute.CHANNEL_STATE ); + } + } + + public class StateMonitor implements Runnable + { + @Override + public void run() + { + if( getState() == State.FADE ) + { + if( mResetTimeout <= System.currentTimeMillis() ) + { + setState( State.END ); + } + } + else if( getState() == State.END ) + { + setState( State.IDLE ); + } + else + { + if( mFadeTimeout <= System.currentTimeMillis() ) + { + setState( State.FADE ); + } + } + } + } + + /** + * Broadcasts the call event to the registered listener + */ + protected void broadcast( CallEvent event ) + { + if( mCallEventListener != null ) + { + mCallEventListener.receive( event ); + } + } + + @Override + public void setCallEventListener( Listener listener ) + { + mCallEventListener = listener; + } + + @Override + public void removeCallEventListener() + { + mCallEventListener = null; + } + + /** + * Broadcasts the channel state attribute change event to all registered + * listeners + */ + protected void broadcast( ChangedAttribute attribute ) + { + if( mChangedAttributeListener != null ) + { + mChangedAttributeListener.receive( attribute ); + } + } + + /** + * Adds the listener to receive channel state attribute change events + */ + @Override + public void setChangedAttributeListener( Listener listener ) + { + mChangedAttributeListener = listener; + } + + /** + * Removes the listener to receive channel state attribute change events + */ + @Override + public void removeChangedAttributeListener() + { + mChangedAttributeListener = null; + } + + /** + * Broadcasts a channel state event to any registered listeners + */ + protected void broadcast( DecoderStateEvent event ) + { + if( mDecoderStateListener != null ) + { + mDecoderStateListener.receive( event ); + } + } + + /** + * Adds a decoder state event listener + */ + @Override + public void setDecoderStateListener( Listener listener ) + { + mDecoderStateListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeDecoderStateListener() + { + mDecoderStateListener = null; + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderStateEventReceiver; + } + + /** + * Broadcasts metadata to the registered listener + */ + protected void broadcast( Metadata metadata ) + { + if( mMetadataListener != null ) + { + mMetadataListener.receive( metadata ); + } + } + + /** + * Adds a metadata listener + */ + @Override + public void setMetadataListener( Listener listener ) + { + mMetadataListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeMetadataListener() + { + mMetadataListener = null; + } + + /** + * 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( TrafficChannelStatusListener listener, + TrafficChannelAllocationEvent allocationEvent ) + { + mTrafficChannelStatusListener = listener; + mTrafficChannelCallEvent = allocationEvent.getCallEvent(); + + /* Broadcast the call event details as metadata for the audio manager */ + String channel = mTrafficChannelCallEvent.getChannel(); + + if( channel != null ) + { + broadcast( new Metadata( MetadataType.CHANNEL, channel, true ) ); + } + + broadcast( new Metadata( MetadataType.PROTOCOL, + mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) ); + + String details = mTrafficChannelCallEvent.getDetails(); + + if( details != null ) + { + broadcast( new Metadata( MetadataType.DETAILS, details, true ) ); + } + + String from = mTrafficChannelCallEvent.getFromID(); + + if( from != null ) + { + broadcast( new Metadata( mTrafficChannelCallEvent.getFromIDType(), + from, mTrafficChannelCallEvent.getFromIDAlias(), true ) ); + } + + String to = mTrafficChannelCallEvent.getToID(); + + if( to != null ) + { + broadcast( new Metadata( mTrafficChannelCallEvent.getToIDType(), to, + mTrafficChannelCallEvent.getToIDAlias(), true ) ); + } + + long frequency = mTrafficChannelCallEvent.getFrequency(); + + if( frequency > 0 ) + { + broadcast( new Metadata( MetadataType.FREQUENCY, + String.valueOf( frequency ), true ) ); + } + + /* Rebroadcast the allocation event so that the internal decoder states + * can self-configure with the call event details */ + broadcast( allocationEvent ); + } + + /** + * DecoderStateEvent receiver wrapper + */ + public class DecoderStateEventReceiver implements Listener + { + @Override + public void receive( DecoderStateEvent event ) + { + if( event.getSource() != this ) + { + switch( event.getEvent() ) + { + case ALWAYS_UNSQUELCH: + broadcast( SquelchState.UNSQUELCH ); + mSquelchLocked = true; + break; + case CONTINUATION: + case DECODE: + if( isEndingState() ) + { + setState( event.getState() ); + } + else + { + updateFadeTimeout(); + } + break; + case END: + setState( State.FADE ); + break; + case RESET: + /* Channel State does not respond to reset events */ + break; + case START: + setState( event.getState() ); + break; + default: + break; + } + } + } + } +} diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java new file mode 100644 index 00000000..34240743 --- /dev/null +++ b/src/module/decode/state/ChannelStatePanel.java @@ -0,0 +1,457 @@ +package module.decode.state; + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JSeparator; + +import module.decode.Decoder; +import module.decode.DecoderFactory; +import module.decode.event.ActivitySummaryFrame; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistManager; +import sample.Listener; +import settings.ColorSetting; +import settings.ColorSetting.ColorSettingName; +import settings.Setting; +import settings.SettingChangeListener; +import settings.SettingsManager; +import controller.channel.Channel; + +public class ChannelStatePanel extends JPanel + implements Listener, SettingChangeListener +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( ChannelStatePanel.class ); + + private static final boolean ENABLED = true; + private static final boolean DISABLED = false; + private static final boolean BROADCAST_CHANGE = true; + + private Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); + private Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); + + protected Color mColorChannelBackground; + protected Color mColorChannelSelected; + protected Color mColorTopCall; + protected Color mColorMiddleCall; + protected Color mColorTopControl; + protected Color mColorMiddleControl; + protected Color mColorTopData; + protected Color mColorMiddleData; + protected Color mColorTopFade; + protected Color mColorMiddleFade; + protected Color mColorTopIdle; + protected Color mColorMiddleIdle; + protected Color mColorTopNoTuner; + protected Color mColorMiddleNoTuner; + protected Color mColorLabelDetails; + protected Color mColorLabelDecoder; + protected Color mColorLabelAuxDecoder; + + private JLabel mStateLabel; + private JLabel mSourceLabel; + private JLabel mChannelLabel; + + private PlaylistManager mPlaylistManager; + protected SettingsManager mSettingsManager; + + private Channel mChannel; + private List mDecoderPanels = new ArrayList<>(); + + /** + * Gui component to a channel state. Provides System, Site and channel + * name, source and channel state visualization. + * + * Updates color background according to current channel state. + */ + public ChannelStatePanel( PlaylistManager playlistManager, + SettingsManager settingsManager, + Channel channel ) + { + mPlaylistManager = playlistManager; + + mSettingsManager = settingsManager; + mSettingsManager.addListener( this ); + + mChannel = channel; + mChannel.getChannelState().setChangedAttributeListener( this ); + + init(); + + for( Decoder decoder: getChannel().getProcessingChain().getDecoders() ) + { + DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoder ); + mDecoderPanels.add( panel ); + add( panel, "grow,span" ); + } + } + + private void init() + { + getColors(); + + setLayout( new MigLayout( "insets 3 2 2 2", "[grow,fill]", "[]0[]0[]") ); + + mStateLabel = new JLabel( mChannel.getChannelState().getState() + .getDisplayValue() ); + mStateLabel.setFont( mFontDecoder ); + mStateLabel.setForeground( mColorLabelDecoder ); + add( mStateLabel ); + + mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); + mSourceLabel.setFont( mFontDetails ); + mSourceLabel.setForeground( mColorLabelDetails ); + add( mSourceLabel ); + + mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); + mChannelLabel.setFont( mFontDetails ); + mChannelLabel.setForeground( mColorLabelDetails ); + add( mChannelLabel, "wrap" ); + } + + public Channel getChannel() + { + return mChannel; + } + + public void dispose() + { + mPlaylistManager = null; + + mSettingsManager.removeListener( this ); + mSettingsManager = null; + + mChannel.getChannelState().removeChangedAttributeListener(); + mChannel = null; + + for( DecoderPanel panel: mDecoderPanels ) + { + panel.dispose(); + remove( panel ); + } + + mDecoderPanels.clear(); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + final ChannelState state = mChannel.getChannelState(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case CHANNEL_STATE: + mStateLabel.setText( state.getState().getDisplayValue() ); + break; + case SOURCE: + mSourceLabel.setText( mChannel.getSourceConfiguration() + .getDescription() ); + break; + case CHANNEL_NAME: + case SITE_NAME: + case SYSTEM_NAME: + mChannelLabel.setText( mChannel.getChannelDisplayName() ); + break; + default: + break; + } + + repaint(); + } + }); + } + + @Override + protected void paintComponent(Graphics g) + { + super.paintComponent( g ); + + setBackground( mColorChannelBackground ); + + Graphics2D g2 = (Graphics2D)g.create(); + + Paint p = null; + + switch( mChannel.getChannelState().getState() ) + { + case CALL: + p = getGradient( mColorTopCall, mColorMiddleCall ); + break; + case CONTROL: + p = getGradient( mColorTopControl, mColorMiddleControl ); + break; + case DATA: + p = getGradient( mColorTopData, mColorMiddleData ); + break; + case FADE: + p = getGradient( mColorTopFade, mColorMiddleFade ); + break; + case END: + case IDLE: + default: + p = getGradient( mColorTopIdle, mColorMiddleIdle ); + break; + } + + g2.setPaint( p ); + g2.fillRect( 0, 0, getWidth(), getHeight() ); + + /* Draw bottom separator line */ + g2.setColor( Color.LIGHT_GRAY ); + g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 ); + + /* Draw channel selected box */ + if( getChannel().isSelected() ) + { + g2.setColor( mColorChannelSelected ); + g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 ); + } + + g2.dispose(); + } + + + private void getColors() + { + mColorChannelBackground = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_BACKGROUND ).getColor(); + mColorChannelSelected = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ).getColor(); + mColorTopCall = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ).getColor(); + mColorMiddleCall = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ).getColor(); + mColorTopControl = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ).getColor(); + mColorMiddleControl = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ).getColor(); + mColorTopData = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ).getColor(); + mColorMiddleData = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ).getColor(); + mColorTopFade = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ).getColor(); + mColorMiddleFade = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ).getColor(); + mColorTopIdle = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ).getColor(); + mColorMiddleIdle = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ).getColor(); + mColorTopNoTuner = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ).getColor(); + mColorMiddleNoTuner = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ).getColor(); + + mColorLabelDetails = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); + mColorLabelDecoder = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); + mColorLabelAuxDecoder = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor(); + } + + private GradientPaint getGradient( Color top, Color middle ) + { + return new GradientPaint( 0f, -50.0f, top, + 0f, (float)getHeight() / 2.2f, middle, true ); + } + + @Override + public void settingChanged( Setting setting ) + { + if( setting instanceof ColorSetting ) + { + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) + { + case CHANNEL_STATE_BACKGROUND: + mColorChannelBackground = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_CALL: + mColorMiddleCall = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_CALL: + mColorTopCall = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL: + mColorMiddleControl = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_CONTROL: + mColorTopControl = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_DATA: + mColorMiddleData = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_DATA: + mColorTopData = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_FADE: + mColorMiddleFade = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_FADE: + mColorTopFade = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE: + mColorMiddleIdle = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_IDLE: + mColorTopIdle = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER: + mColorMiddleNoTuner = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER: + mColorTopNoTuner = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_AUX_DECODER: + mColorLabelAuxDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DECODER: + mColorLabelDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DETAILS: + mColorLabelDetails = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_SELECTED_CHANNEL: + mColorChannelSelected = colorSetting.getColor(); + repaint(); + break; + default: + break; + } + } + } + + @Override + public void settingDeleted( Setting setting ) {} + + public JMenu getContextMenu() + { + JMenu menu = new JMenu( "Channel: " + mChannel.getName() ); + + if( mChannel.getEnabled() ) + { + JMenuItem disable = new JMenuItem( "Disable" ); + disable.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mChannel.setEnabled( DISABLED, BROADCAST_CHANGE ); + + if( mPlaylistManager != null ) + { + mPlaylistManager.save(); + } + } + } ); + + 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(); + + for( Decoder decoder: mChannel.getProcessingChain().getDecoders() ) + { + sb.append( decoder.getDecoderState().getActivitySummary() ); + } + + new ActivitySummaryFrame( sb.toString(), ChannelStatePanel.this ); + } + } ); + + menu.add( actySummaryItem ); + } + else + { + JMenuItem enable = new JMenuItem( "Enable" ); + enable.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mChannel.setEnabled( ENABLED, BROADCAST_CHANGE ); + + if( mPlaylistManager != null ) + { + mPlaylistManager.save(); + } + } + } ); + + menu.add( enable ); + } + + menu.add( new JSeparator() ); + + JMenuItem deleteItem = new JMenuItem( "Delete" ); + deleteItem.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + dispose(); + + if( mPlaylistManager != null ) + { + mPlaylistManager.save(); + } + } + } ); + + menu.add( deleteItem ); + + return menu; + } + +} diff --git a/src/controller/state/ChannelStateSelectionListener.java b/src/module/decode/state/ChannelStateSelectionListener.java similarity index 97% rename from src/controller/state/ChannelStateSelectionListener.java rename to src/module/decode/state/ChannelStateSelectionListener.java index 2e4c611a..c0496acf 100644 --- a/src/controller/state/ChannelStateSelectionListener.java +++ b/src/module/decode/state/ChannelStateSelectionListener.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.state; +package module.decode.state; public interface ChannelStateSelectionListener diff --git a/src/controller/state/AuxStatePanel.java b/src/module/decode/state/DecoderPanel.java similarity index 50% rename from src/controller/state/AuxStatePanel.java rename to src/module/decode/state/DecoderPanel.java index 8d3bdba9..bd629754 100644 --- a/src/controller/state/AuxStatePanel.java +++ b/src/module/decode/state/DecoderPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,108 +15,79 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.state; +package module.decode.state; import java.awt.Color; import java.awt.Font; -import javax.swing.ImageIcon; -import javax.swing.JLabel; import javax.swing.JPanel; -import net.miginfocom.swing.MigLayout; +import module.decode.Decoder; import sample.Listener; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; -import alias.Alias; -import controller.state.ChannelState.ChangedAttribute; -public abstract class AuxStatePanel extends JPanel - implements Listener, - SettingChangeListener +public abstract class DecoderPanel extends JPanel + implements Listener, SettingChangeListener { - private static final long serialVersionUID = 1L; - + private static final long serialVersionUID = 1L; + protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); + protected Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - + protected Color mColorLabelDetails; + protected Color mColorLabelDecoder; protected Color mColorLabelAuxDecoder; - - protected SettingsManager mSettingsManager; - protected AuxChannelState mAuxChannelState; - - public AuxStatePanel( SettingsManager settingsManager, - AuxChannelState auxChannelState ) - { - mSettingsManager = settingsManager; - mAuxChannelState = auxChannelState; - - /* Register to receive event updates */ - mAuxChannelState.addListener( this ); - - /* Register to receive settings updates */ - mSettingsManager.addListener( this ); - - getColors(); - - setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); - } - - public void dispose() - { - /* Deregister from changed settings events */ - mSettingsManager.removeListener( this ); - mSettingsManager = null; - - mAuxChannelState = null; - } - - protected void addLabel( JLabel label, boolean wrap ) + + protected SettingsManager mSettingsManager; + protected Decoder mDecoder; + + public DecoderPanel( SettingsManager settingsManager, Decoder decoder ) { - label.setFont( mFontAuxDecoder ); - label.setForeground( mColorLabelAuxDecoder ); + mSettingsManager = settingsManager; + mSettingsManager.addListener( this ); - if( wrap ) - { - add( label, "wrap" ); - } - else - { - add( label ); - } + mDecoder = decoder; + + mDecoder.getDecoderState().setChangedAttributeListener( this ); } - - protected void updateAliasLabel( JLabel label, Alias alias ) + + public void dispose() { - if( alias != null ) - { - label.setText( alias.getName() ); - - String iconName = alias.getIconName(); - - ImageIcon icon = mSettingsManager.getImageIcon( iconName, 12 ); - - label.setIcon( icon ); - } - else - { - label.setText( null ); - label.setIcon( null ); - } + mSettingsManager.removeListener( this ); + mSettingsManager = null; + + mDecoder.getDecoderState().removeChangedAttributeListener(); + mDecoder = null; + } + + public Decoder getDecoder() + { + return mDecoder; + } + + protected void init() + { + getColors(); + + setOpaque( false ); //Use the parent panel's background color } private void getColors() { mColorLabelDetails = mSettingsManager.getColorSetting( ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); + mColorLabelDecoder = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); mColorLabelAuxDecoder = mSettingsManager.getColorSetting( ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor(); } - + + @Override public void settingChanged( Setting setting ) { @@ -126,13 +97,19 @@ public abstract class AuxStatePanel extends JPanel switch( colorSetting.getColorSettingName() ) { - case CHANNEL_STATE_LABEL_AUX_DECODER: - mColorLabelAuxDecoder = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_DETAILS: - mColorLabelDetails = colorSetting.getColor(); - repaint(); + case CHANNEL_STATE_LABEL_AUX_DECODER: + mColorLabelAuxDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DECODER: + mColorLabelDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DETAILS: + mColorLabelDetails = colorSetting.getColor(); + repaint(); + break; + default: break; } } diff --git a/src/module/decode/state/DecoderState.java b/src/module/decode/state/DecoderState.java new file mode 100644 index 00000000..7540950d --- /dev/null +++ b/src/module/decode/state/DecoderState.java @@ -0,0 +1,186 @@ +package module.decode.state; + +import message.Message; +import module.decode.event.ActivitySummaryProvider; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import sample.Listener; +import alias.AliasList; + +/** + * Channel state monitors the stream of decoded messages produced by the + * decoder and broadcasts call events as they occur within the decoded message activity. + * + * Provides access to a textual activity summary of events observed. + */ +public abstract class DecoderState implements ActivitySummaryProvider, + Listener, + ICallEventProvider, + IChangedAttributeProvider, + IDecoderStateEventListener, + IDecoderStateEventProvider +{ + private Listener mCallEventListener; + private Listener mChangedAttributeListener; + private Listener mDecoderStateListener; + + private DecoderStateEventListener mDecoderStateEventListener = + new DecoderStateEventListener(); + + protected CallEvent mCurrentCallEvent; + + private AliasList mAliasList; + + public DecoderState( AliasList aliasList ) + { + mAliasList = aliasList; + } + + public AliasList getAliasList() + { + return mAliasList; + } + + public boolean hasAliasList() + { + return mAliasList != null; + } + + /** + * Reset the decoder state to prepare for processing a different sample + * source + */ + public abstract void reset(); + + /** + * Allow the decoder to perform any setup actions + */ + public abstract void init(); + + /** + * Implements the IDecoderStateEventListener interface to receive state + * reset events. + */ + public abstract void receiveDecoderStateEvent( DecoderStateEvent event ); + + /** + * Disposes any resources or pointers held by this instance to prepare for + * garbage collection + */ + public void dispose() + { + mCallEventListener = null; + mChangedAttributeListener = null; + mDecoderStateListener = null; + + mAliasList = null; + } + + /** + * Activity Summary - textual summary of activity observed by the channel state. + */ + public abstract String getActivitySummary(); + + /** + * Broadcasts a call event to any registered listeners + */ + protected void broadcast( CallEvent event ) + { + if( mCallEventListener != null ) + { + mCallEventListener.receive( event ); + } + } + + /** + * Adds a call event listener + */ + @Override + public void setCallEventListener( Listener listener ) + { + mCallEventListener = listener; + } + + /** + * Removes the call event listener + */ + @Override + public void removeCallEventListener() + { + mCallEventListener = null; + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderStateEventListener; + } + + private class DecoderStateEventListener implements Listener + { + @Override + public void receive( DecoderStateEvent event ) + { + receiveDecoderStateEvent( event ); + } + } + + /** + * Broadcasts the channel state attribute change event to all registered + * listeners + */ + protected void broadcast( ChangedAttribute attribute ) + { + if( mChangedAttributeListener != null ) + { + mChangedAttributeListener.receive( attribute ); + } + } + + /** + * Adds the listener to receive channel state attribute change events + */ + @Override + public void setChangedAttributeListener( Listener listener ) + { + mChangedAttributeListener = listener; + } + + /** + * Removes the listener to receive channel state attribute change events + */ + @Override + public void removeChangedAttributeListener() + { + mChangedAttributeListener = null; + } + + /** + * Broadcasts a channel state event to any registered listeners + */ + protected void broadcast( DecoderStateEvent event ) + { + if( mDecoderStateListener != null ) + { + mDecoderStateListener.receive( event ); + } + } + + /** + * Adds a decoder state event listener + */ + @Override + public void setDecoderStateListener( Listener listener ) + { + mDecoderStateListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeDecoderStateListener() + { + mDecoderStateListener = null; + } +} diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/module/decode/state/DecoderStateEvent.java new file mode 100644 index 00000000..ae1a485e --- /dev/null +++ b/src/module/decode/state/DecoderStateEvent.java @@ -0,0 +1,55 @@ +package module.decode.state; + +public class DecoderStateEvent +{ + private Object mSource; + private Event mEvent; + private State mState; + private long mFrequency; + + public DecoderStateEvent( Object source, Event event, State state ) + { + mSource = source; + mEvent = event; + mState = state; + } + + public DecoderStateEvent( Object source, Event event, State state, long frequency ) + { + this( source, event, state ); + + mFrequency = frequency; + } + + public Object getSource() + { + return mSource; + } + + public Event getEvent() + { + return mEvent; + } + + public State getState() + { + return mState; + } + + public long getFrequency() + { + return mFrequency; + } + + public enum Event + { + ALWAYS_UNSQUELCH, + CONTINUATION, + DECODE, + END, + RESET, + SOURCE_FREQUENCY, + START, + TRAFFIC_CHANNEL_ALLOCATION; + } +} diff --git a/src/module/decode/state/IChangedAttributeListener.java b/src/module/decode/state/IChangedAttributeListener.java new file mode 100644 index 00000000..04d3ce66 --- /dev/null +++ b/src/module/decode/state/IChangedAttributeListener.java @@ -0,0 +1,8 @@ +package module.decode.state; + +import sample.Listener; + +public interface IChangedAttributeListener +{ + public Listener getChangedAttributeListener(); +} diff --git a/src/module/decode/state/IChangedAttributeProvider.java b/src/module/decode/state/IChangedAttributeProvider.java new file mode 100644 index 00000000..283c3e6b --- /dev/null +++ b/src/module/decode/state/IChangedAttributeProvider.java @@ -0,0 +1,9 @@ +package module.decode.state; + +import sample.Listener; + +public interface IChangedAttributeProvider +{ + public void setChangedAttributeListener( Listener listener ); + public void removeChangedAttributeListener(); +} diff --git a/src/module/decode/state/IDecoderStateEventListener.java b/src/module/decode/state/IDecoderStateEventListener.java new file mode 100644 index 00000000..72b9eb1c --- /dev/null +++ b/src/module/decode/state/IDecoderStateEventListener.java @@ -0,0 +1,8 @@ +package module.decode.state; + +import sample.Listener; + +public interface IDecoderStateEventListener +{ + public Listener getDecoderStateListener(); +} diff --git a/src/module/decode/state/IDecoderStateEventProvider.java b/src/module/decode/state/IDecoderStateEventProvider.java new file mode 100644 index 00000000..afd8c2d0 --- /dev/null +++ b/src/module/decode/state/IDecoderStateEventProvider.java @@ -0,0 +1,9 @@ +package module.decode.state; + +import sample.Listener; + +public interface IDecoderStateEventProvider +{ + public void setDecoderStateListener( Listener listener ); + public void removeDecoderStateListener(); +} diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java new file mode 100644 index 00000000..2820429d --- /dev/null +++ b/src/module/decode/state/State.java @@ -0,0 +1,83 @@ +package module.decode.state; + +public enum State +{ + IDLE( "IDLE" ) + { + @Override + public boolean canChangeTo( State state ) + { + return true; + } + }, + CALL( "CALL" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state == CALL || + state == CONTROL || + state == DATA || + state == ENCRYPTED || + state == FADE; + } + }, + DATA( "DATA" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state == CALL || + state == CONTROL || + state == DATA || + state == State.ENCRYPTED || + state == FADE; + } + }, + ENCRYPTED( "ENCRYPTED" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state == FADE; + } + }, + CONTROL( "CONTROL" ) + { + @Override + public boolean canChangeTo( State state ) + { + return true; + } + }, + FADE( "FADE" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state != FADE; //All states except fade allowed + } + }, + END( "END" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state != FADE; //Fade is only disallowed state + } + }; + + private String mDisplayValue; + + private State( String displayValue ) + { + mDisplayValue = displayValue; + } + + public abstract boolean canChangeTo( State state ); + + public String getDisplayValue() + { + return mDisplayValue; + } +} diff --git a/src/module/decode/state/TrafficChannelAllocationEvent.java b/src/module/decode/state/TrafficChannelAllocationEvent.java new file mode 100644 index 00000000..14441fde --- /dev/null +++ b/src/module/decode/state/TrafficChannelAllocationEvent.java @@ -0,0 +1,25 @@ +package module.decode.state; + +import 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/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java new file mode 100644 index 00000000..cf3021cf --- /dev/null +++ b/src/module/decode/state/TrafficChannelManager.java @@ -0,0 +1,255 @@ +package module.decode.state; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.event.ICallEventProvider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.config.SourceConfigTuner; +import source.tuner.TunerChannel; +import source.tuner.TunerChannel.Type; +import controller.ResourceManager; +import controller.channel.Channel; +import controller.channel.Channel.ChannelType; +import controller.site.Site; +import controller.system.System; + +public class TrafficChannelManager extends Module + implements ICallEventProvider, IDecoderStateEventListener +{ + private final static Logger mLog = LoggerFactory.getLogger( TrafficChannelManager.class ); + + 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 DecodeConfiguration mDecodeConfiguration; + private ResourceManager mResourceManager; + private long mTrafficChannelTimeout; + + /** + * 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 resourceManager - resource manager + * + * @param decodeConfiguration - decoder configuration to use for each + * traffic channel allocation. + * + * @param trafficChannelTimeout - millisecond call timer limit used when an + * end of call signalling event is not decoded. + * + * @param trafficChannelPoolSize - maximum number of allocated traffic channels + * in the pool + */ + public TrafficChannelManager( ResourceManager resourceManager, + DecodeConfiguration decodeConfiguration, + long trafficChannelTimeout, + int trafficChannelPoolSize ) + { + mResourceManager = resourceManager; + mDecodeConfiguration = decodeConfiguration; + mTrafficChannelTimeout = trafficChannelTimeout; + mTrafficChannelPoolMaximumSize = trafficChannelPoolSize; + } + + @Override + public void dispose() + { + mCallEventListener = null; + mResourceManager = null; + mDecodeConfiguration = null; + } + + 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.setResourceManager( mResourceManager ); + + channel.setTrafficChannelTimeout( mTrafficChannelTimeout ); + + mTrafficChannelPool.add( channel ); + } + + /* If we have a configured channel, start it and track it */ + if( channel != null ) + { + channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) ); + + channel.setSystem( new System( "Traffic" ), false ); + channel.setSite( new Site( "Channel" ), false ); + channel.setName( channelNumber ); + + channel.setEnabled( true ); + + /* Check the channel obtained a source and is currently processing */ + if( channel.isProcessing() ) + { + mTrafficChannelsInUse.put( channelNumber, channel ); + } + else + { + channel.setEnabled( false ); + + channel.setSystem( null, false ); + channel.setSite( null, false ); + channel.setName( "Traffic" ); + } + } + } + + return channel; + } + + private void process( TrafficChannelAllocationEvent event ) + { + if( mCallEventListener != null ) + { + CallEvent callEvent = event.getCallEvent(); + + /* Check for duplicate events and suppress */ + if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) ) + { + return; + } + + long frequency = callEvent.getFrequency(); + + if( frequency > 0 ) + { + Channel channel = getChannel( callEvent.getChannel(), + new TunerChannel( Type.TRAFFIC, frequency, + mDecodeConfiguration.getDecoderType() + .getChannelBandwidth() ) ); + + if( channel != null ) + { + if( channel.isProcessing() ) + { + ChannelState state = channel.getChannelState(); + + /* Register as a listener to be notified when the call + * is completed */ + state.configureAsTrafficChannel( + new TrafficChannelStatusListener( this, + callEvent.getChannel() ), event ); + + /* Set state to call so that audio squelch state is correct */ + state.setState( State.CALL ); + } + else + { + callEvent.setCallEventType( CallEventType.CALL_DETECT ); + } + } + else + { + callEvent.setCallEventType( CallEventType.CALL_DETECT ); + } + } + else + { + callEvent.setCallEventType( CallEventType.CALL_DETECT ); + } + + mCallEventListener.receive( callEvent ); + } + } + + @Override + public Listener getDecoderStateListener() + { + return mEventListener; + } + + @Override + public void setCallEventListener( Listener listener ) + { + mCallEventListener = listener; + } + + @Override + public void removeCallEventListener() + { + mCallEventListener = null; + } + + @Override + public void init() + { + + } + + /** + * 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 ) + { + if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) ) + { + Channel channel = mTrafficChannelsInUse.get( channelNumber ); + + mTrafficChannelsInUse.remove( channelNumber ); + + /* Disable the channel and broadcast a notification */ + channel.setEnabled( false, true ); + } + } + + /** + * 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; + } + } + } +} diff --git a/src/module/decode/state/TrafficChannelStatusListener.java b/src/module/decode/state/TrafficChannelStatusListener.java new file mode 100644 index 00000000..e1b31a58 --- /dev/null +++ b/src/module/decode/state/TrafficChannelStatusListener.java @@ -0,0 +1,21 @@ +package module.decode.state; + +public class TrafficChannelStatusListener +{ + private TrafficChannelManager mTrafficChannelManager; + private String mChannelNumber; + + public TrafficChannelStatusListener( TrafficChannelManager manager, String channelNumber ) + { + mTrafficChannelManager = manager; + mChannelNumber = channelNumber; + } + + public void callEnd() + { + if( mTrafficChannelManager != null ) + { + mTrafficChannelManager.callEnd( mChannelNumber ); + } + } +} diff --git a/src/decode/tait/Tait1200ANIMessage.java b/src/module/decode/tait/Tait1200ANIMessage.java similarity index 99% rename from src/decode/tait/Tait1200ANIMessage.java rename to src/module/decode/tait/Tait1200ANIMessage.java index 968ef2ad..08eeb6d5 100644 --- a/src/decode/tait/Tait1200ANIMessage.java +++ b/src/module/decode/tait/Tait1200ANIMessage.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/tait/Tait1200ANIMessageProcessor.java b/src/module/decode/tait/Tait1200ANIMessageProcessor.java similarity index 78% rename from src/decode/tait/Tait1200ANIMessageProcessor.java rename to src/module/decode/tait/Tait1200ANIMessageProcessor.java index e091b620..34a1a9df 100644 --- a/src/decode/tait/Tait1200ANIMessageProcessor.java +++ b/src/module/decode/tait/Tait1200ANIMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,14 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -32,7 +31,7 @@ public class Tait1200ANIMessageProcessor implements Listener private final static Logger mLog = LoggerFactory.getLogger( Tait1200ANIMessageProcessor.class ); - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -43,22 +42,26 @@ public class Tait1200ANIMessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; + mAliasList = null; } @Override public void receive( BinaryMessage buffer ) { - mBroadcaster.receive( new Tait1200ANIMessage( buffer, mAliasList ) ); + if( mMessageListener != null ) + { + mMessageListener.receive( new Tait1200ANIMessage( buffer, mAliasList ) ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } public void removeMessageListener( Listener listener ) { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/tait/Tait1200CallEvent.java b/src/module/decode/tait/Tait1200CallEvent.java similarity index 95% rename from src/decode/tait/Tait1200CallEvent.java rename to src/module/decode/tait/Tait1200CallEvent.java index 73aa69ff..90bd6c53 100644 --- a/src/decode/tait/Tait1200CallEvent.java +++ b/src/module/decode/tait/Tait1200CallEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class Tait1200CallEvent extends CallEvent { diff --git a/src/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java similarity index 69% rename from src/decode/tait/Tait1200Decoder.java rename to src/module/decode/tait/Tait1200Decoder.java index 455ba341..0a328bda 100644 --- a/src/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,35 +15,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Broadcaster; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; /** * TAIT 1200 - 1200 baud 2FSK decoder */ -public class Tait1200Decoder extends Decoder implements Instrumentable +public class Tait1200Decoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int DECIMATED_SAMPLE_RATE = 24000; @@ -56,8 +58,6 @@ public class Tait1200Decoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = @@ -65,24 +65,23 @@ public class Tait1200Decoder extends Decoder implements Instrumentable private FSK2Decoder mFSKDecoder; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private MessageFramer mMessageFramerGPS; - private MessageFramer mMessageFramerSELCALL; + private MessageFramer mMessageFramerANI; private Broadcaster mFSKBroadcaster = new Broadcaster(); private Tait1200GPSMessageProcessor mMessageAProcessor; private Tait1200ANIMessageProcessor mMessageBProcessor; public Tait1200Decoder( AliasList aliasList ) { - super( SampleType.REAL ); + super( new Tait1200DecoderState( aliasList ) ); - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true ); - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); mDecimationFilter.setListener( mBandPassFilter ); mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE, @@ -94,20 +93,20 @@ public class Tait1200Decoder extends Decoder implements Instrumentable mMessageFramerGPS = new MessageFramer( SyncPattern.TAIT_CCDI_GPS_MESSAGE.getPattern(), MESSAGE_LENGTH ); - mMessageFramerSELCALL = new MessageFramer( + mMessageFramerANI = new MessageFramer( SyncPattern.TAIT_SELCAL_MESSAGE.getPattern(), MESSAGE_LENGTH ); mFSKBroadcaster.addListener( mMessageFramerGPS ); - mFSKBroadcaster.addListener( mMessageFramerSELCALL ); + mFSKBroadcaster.addListener( mMessageFramerANI ); mMessageAProcessor = new Tait1200GPSMessageProcessor( aliasList ); mMessageBProcessor = new Tait1200ANIMessageProcessor( aliasList ); mMessageFramerGPS.addMessageListener( mMessageAProcessor ); - mMessageFramerSELCALL.addMessageListener( mMessageBProcessor ); + mMessageFramerANI.addMessageListener( mMessageBProcessor ); - mMessageAProcessor.addMessageListener( this ); - mMessageBProcessor.addMessageListener( this ); + mMessageAProcessor.setMessageListener( mMessageBroadcaster ); + mMessageBProcessor.setMessageListener( mMessageBroadcaster ); } public void dispose() @@ -118,26 +117,11 @@ public class Tait1200Decoder extends Decoder implements Instrumentable mBandPassFilter.dispose(); mFSKDecoder.dispose(); mMessageFramerGPS.dispose(); - mMessageFramerSELCALL.dispose(); + mMessageFramerANI.dispose(); mMessageAProcessor.dispose(); mMessageBProcessor.dispose(); } - @Override - public DecoderType getType() - { - return DecoderType.TAIT_1200; - } - - /** - * Returns a float listener interface for connecting this decoder to a - * float stream provider - */ - public RealSampleListener getRealReceiver() - { - return (RealSampleListener)mDecimationFilter; - } - @Override public List getTaps() { @@ -145,7 +129,6 @@ public class Tait1200Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); @@ -163,12 +146,8 @@ public class Tait1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - addRealSampleListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatTap bpTap = (FloatTap)tap; + FloatBufferTap bpTap = (FloatBufferTap)tap; mBandPassFilter.setListener( bpTap ); bpTap.setListener( mFSKDecoder ); break; @@ -186,10 +165,6 @@ public class Tait1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - removeRealListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -200,15 +175,14 @@ public class Tait1200Decoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } + public Listener getRealBufferListener() + { + return mDecimationFilter; + } @Override - public IAudioOutput getAudioOutput() + public DecoderType getDecoderType() { - // Not implemented - return null; + return DecoderType.TAIT_1200; } } diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java new file mode 100644 index 00000000..7f0a8e71 --- /dev/null +++ b/src/module/decode/tait/Tait1200DecoderState.java @@ -0,0 +1,236 @@ +/******************************************************************************* + * 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 module.decode.tait; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.TreeSet; + +import org.jdesktop.swingx.mapviewer.GeoPosition; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import alias.Alias; +import alias.AliasList; + +public class Tait1200DecoderState extends DecoderState +{ + private TreeSet mIdents = new TreeSet(); + + private String mFrom; + private Alias mFromAlias; + + private String mTo; + private Alias mToAlias; + + private String mMessage; + private String mMessageType; + + public Tait1200DecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void reset() + { + mIdents.clear(); + + resetState(); + } + + private void resetState() + { + setFromID( null ); + setFromIDAlias( null ); + setToID( null ); + setToIDAlias( null ); + setMessage( null ); + setMessageType( null ); + } + + @Override + public void receive( Message message ) + { + if( message instanceof Tait1200GPSMessage ) + { + Tait1200GPSMessage gps = (Tait1200GPSMessage)message; + + setFromID( gps.getFromID() ); + setFromIDAlias( gps.getFromIDAlias() ); + mIdents.add( gps.getFromID() ); + + setToID( gps.getToID() ); + setToIDAlias( gps.getToIDAlias() ); + mIdents.add( gps.getToID()); + + GeoPosition position = gps.getGPSLocation(); + + if( position != null ) + { + setMessage( gps.getGPSLocation().toString().replace( "[", "" ) + .replace( "]", "" ) ); + } + setMessageType( "GPS" ); + } + else if( message instanceof Tait1200ANIMessage ) + { + Tait1200ANIMessage ani = (Tait1200ANIMessage)message; + + setFromID( ani.getFromID() ); + setFromIDAlias( ani.getFromIDAlias() ); + mIdents.add( ani.getFromID() ); + + setToID( ani.getToID() ); + setToIDAlias( ani.getToIDAlias() ); + mIdents.add( ani.getToID()); + + setMessage( null ); + setMessageType( "ANI" ); + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tTait-1200I\n\n" ); + + if( !mIdents.isEmpty() ) + { + sb.append( "Radio Identifiers:\n" ); + + List idents = new ArrayList( mIdents ); + + Collections.sort( idents ); + + for( String ident: idents ) + { + 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" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization required */ + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + default: + break; + } + } + + public String getFromID() + { + return mFrom; + } + + public void setFromID( String from ) + { + mFrom = from; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromIDAlias() + { + return mFromAlias; + } + + public void setFromIDAlias( Alias alias ) + { + mFromAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getToID() + { + return mTo; + } + + public void setToID( String to ) + { + mTo = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToIDAlias() + { + return mToAlias; + } + + public void setToIDAlias( Alias alias ) + { + mToAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage( String message ) + { + mMessage = message; + broadcast( ChangedAttribute.MESSAGE ); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType( String type ) + { + mMessageType = type; + broadcast( ChangedAttribute.MESSAGE_TYPE ); + } +} diff --git a/src/decode/tait/Tait1200GPSMessage.java b/src/module/decode/tait/Tait1200GPSMessage.java similarity index 99% rename from src/decode/tait/Tait1200GPSMessage.java rename to src/module/decode/tait/Tait1200GPSMessage.java index f68e8207..990341cc 100644 --- a/src/decode/tait/Tait1200GPSMessage.java +++ b/src/module/decode/tait/Tait1200GPSMessage.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/tait/Tait1200GPSMessageProcessor.java b/src/module/decode/tait/Tait1200GPSMessageProcessor.java similarity index 75% rename from src/decode/tait/Tait1200GPSMessageProcessor.java rename to src/module/decode/tait/Tait1200GPSMessageProcessor.java index 6e1887b0..2d8d732a 100644 --- a/src/decode/tait/Tait1200GPSMessageProcessor.java +++ b/src/module/decode/tait/Tait1200GPSMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,14 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -32,7 +31,7 @@ public class Tait1200GPSMessageProcessor implements Listener private final static Logger mLog = LoggerFactory.getLogger( Tait1200GPSMessageProcessor.class ); - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -43,22 +42,26 @@ public class Tait1200GPSMessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; + mAliasList = null; } @Override public void receive( BinaryMessage buffer ) { - mBroadcaster.receive( new Tait1200GPSMessage( buffer, mAliasList ) ); + if( mMessageListener != null ) + { + mMessageListener.receive( new Tait1200GPSMessage( buffer, mAliasList ) ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/config/DecodeConfigP25Phase1.java b/src/module/decode/tait/Tait1200MessageFilter.java similarity index 56% rename from src/decode/config/DecodeConfigP25Phase1.java rename to src/module/decode/tait/Tait1200MessageFilter.java index 7de83b52..70d9e4cd 100644 --- a/src/decode/config/DecodeConfigP25Phase1.java +++ b/src/module/decode/tait/Tait1200MessageFilter.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,38 +15,38 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.tait; -import javax.xml.bind.annotation.XmlAttribute; +import java.util.Collections; +import java.util.List; -import decode.DecoderType; -import decode.p25.P25Decoder; -import decode.p25.P25Decoder.Modulation; +import message.Message; +import filter.Filter; +import filter.FilterElement; -public class DecodeConfigP25Phase1 extends DecodeConfiguration +public class Tait1200MessageFilter extends Filter { - private P25Decoder.Modulation mModulation = Modulation.C4FM; - public DecodeConfigP25Phase1() - { - super( DecoderType.P25_PHASE1 ); - - setAFC( false ); - } - - public boolean supportsAFC() - { - return false; - } - - @XmlAttribute( name = "modulation" ) - public P25Decoder.Modulation getModulation() + public Tait1200MessageFilter() { - return mModulation; + super( "Tait-1200 Message Filter" ); } - public void setModulation( P25Decoder.Modulation modulation ) - { - mModulation = modulation; - } + @Override + public boolean passes( Message message ) + { + return mEnabled && canProcess( message ); + } + + @Override + public boolean canProcess( Message message ) + { + return message instanceof Tait1200GPSMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } } diff --git a/src/decode/fleetsync2/FleetsyncPanel.java b/src/module/decode/tait/Tait1200Panel.java similarity index 73% rename from src/decode/fleetsync2/FleetsyncPanel.java rename to src/module/decode/tait/Tait1200Panel.java index a601baab..88e86c57 100644 --- a/src/decode/fleetsync2/FleetsyncPanel.java +++ b/src/module/decode/tait/Tait1200Panel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * 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 @@ -15,24 +15,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.tait; import java.awt.EventQueue; import javax.swing.JLabel; +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; -public class FleetsyncPanel extends AuxStatePanel +public class Tait1200Panel extends DecoderPanel { private static final long serialVersionUID = 1L; - private static final String PROTOCOL = "FSync II"; + private static final String PROTOCOL = "TAIT-1200"; private JLabel mFromLabel = new JLabel( " " ); private JLabel mFrom = new JLabel(); @@ -46,10 +47,9 @@ public class FleetsyncPanel extends AuxStatePanel private JLabel mMessage = new JLabel(); private JLabel mMessageType = new JLabel(); - public FleetsyncPanel( SettingsManager settingsManager, - FleetsyncChannelState state ) + public Tait1200Panel( SettingsManager settingsManager, Tait1200Decoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } @@ -59,28 +59,30 @@ public class FleetsyncPanel extends AuxStatePanel super.dispose(); } - public FleetsyncChannelState getState() + public Tait1200DecoderState getState() { - return (FleetsyncChannelState)mAuxChannelState; + return (Tait1200DecoderState)getDecoder().getDecoderState(); } - private void init() + protected void init() { - setOpaque( false ); //Use the parent panel's background color + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); - addLabel( mFromLabel, false ); - addLabel( mFrom, false ); - addLabel( mFromAlias, true ); + add( mFromLabel ); + add( mFrom ); + add( mFromAlias, "wrap" ); - addLabel( mToLabel, false ); - addLabel( mTo, false ); - addLabel( mToAlias, true ); + add( mToLabel ); + add( mTo ); + add( mToAlias, "wrap" ); - addLabel( mProtocol, false ); - addLabel( mMessageType, false ); - addLabel( mMessage, true ); + add( mProtocol ); + add( mMessageType ); + add( mMessage, "wrap" ); } @Override @@ -94,7 +96,7 @@ public class FleetsyncPanel extends AuxStatePanel switch( changedAttribute ) { case FROM_TALKGROUP: - String from = getState().getFleetIDFrom(); + String from = getState().getFromID(); if( from != null ) { @@ -107,11 +109,15 @@ public class FleetsyncPanel extends AuxStatePanel mFrom.setText( from ); break; case FROM_TALKGROUP_ALIAS: - updateAliasLabel( mFromAlias, - getState().getFleetIDFromAlias() ); + Alias fromAlias = getState().getFromIDAlias(); + + if( fromAlias != null ) + { + mFromAlias.setText( fromAlias.getName() ); + } break; case TO_TALKGROUP: - String to = getState().getFleetIDTo(); + String to = getState().getToID(); if( to != null ) { @@ -124,8 +130,12 @@ public class FleetsyncPanel extends AuxStatePanel mTo.setText( to ); break; case TO_TALKGROUP_ALIAS: - updateAliasLabel( mToAlias, - getState().getFleetIDToAlias() ); + Alias toAlias = getState().getToIDAlias(); + + if( toAlias != null ) + { + mToAlias.setText( toAlias.getName() ); + } break; case MESSAGE: mMessage.setText( getState().getMessage() ); @@ -133,6 +143,8 @@ public class FleetsyncPanel extends AuxStatePanel case MESSAGE_TYPE: mMessageType.setText( getState().getMessageType() ); break; + default: + break; } repaint(); diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java new file mode 100644 index 00000000..5476529c --- /dev/null +++ b/src/module/demodulate/am/AMDemodulatorModule.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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 module.demodulate.am; + +import module.Module; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; +import sample.real.IRealBufferProvider; +import sample.real.RealBuffer; +import dsp.am.AMDemodulator_CB; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.gain.AutomaticGainControl_RB; + +public class AMDemodulatorModule extends Module + implements IComplexBufferListener, IRealBufferProvider +{ + private static final int SAMPLE_RATE = 48000; + + private ComplexFIRFilter_CB_CB mIQFilter; + private AMDemodulator_CB mDemodulator; + private RealFIRFilter_RB_RB mLowPassFilter; + private AutomaticGainControl_RB mAGC; + + /** + * FM Demodulator with integrated DC removal and automatic frequency + * correction control. + * + * @param pass - pass frequency for IQ filtering prior to demodulation. This + * frequency should be half of the signal bandwidth since the filter will + * be applied against each of the inphase and quadrature signals and the + * combined pass bandwidth will be twice this value. + * + * @param stop - stop frequency for IQ filtering prior to demodulation. + * + * @param maxFrequencyCorrection - defines the maximum frequency +/- that + * the correction controller can adjust from the center tuned frequency. + * Set this value to 0 for no frequency correction control. + */ + public AMDemodulatorModule() + { + mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( + SAMPLE_RATE, 6500, 73, WindowType.HAMMING ), 1.0f ); + + mDemodulator = new AMDemodulator_CB( 500.0f ); + mIQFilter.setListener( mDemodulator ); + + mLowPassFilter = new RealFIRFilter_RB_RB( + FilterFactory.getLowPass( 48000, 3000, 31, WindowType.COSINE ), 1.0f ); + mDemodulator.setListener( mLowPassFilter ); + + mAGC = new AutomaticGainControl_RB(); + mLowPassFilter.setListener( mAGC ); + } + + @Override + public Listener getComplexBufferListener() + { + return mIQFilter; + } + + @Override + public void dispose() + { + mIQFilter.dispose(); + mIQFilter = null; + + mDemodulator.dispose(); + mDemodulator = null; + + mLowPassFilter.dispose(); + mLowPassFilter = null; + + mAGC.dispose(); + mAGC = null; + } + + @Override + public void init() + { + } + + @Override + public void setRealBufferListener( Listener listener ) + { + mAGC.setListener( listener ); + } + + @Override + public void removeRealBufferListener() + { + mAGC.removeListener(); + } +} diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java new file mode 100644 index 00000000..299b523b --- /dev/null +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * 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 module.demodulate.fm; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; +import sample.real.IRealBufferProvider; +import sample.real.RealBuffer; +import source.tuner.frequency.AutomaticFrequencyControl_RB; +import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.IFrequencyCorrectionController; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.dc.AveragingDCRemovalFilter_RB; +import dsp.filter.dc.DCRemovalFilter_RB; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.fm.FMDemodulator_CB; + +public class FMDemodulatorModule extends Module implements IComplexBufferListener, + IFrequencyCorrectionController, IRealBufferProvider +{ + private final static Logger mLog = LoggerFactory.getLogger( FMDemodulatorModule.class ); + + /* Determines responsiveness of DC filter to frequency changes */ + private static final float DC_REMOVAL_RATIO = 0.000003f; + + private static final int SAMPLE_RATE = 48000; + + private ComplexFIRFilter_CB_CB mIQFilter; + private FMDemodulator_CB mDemodulator; + private Broadcaster mUnfilteredBufferBroadcaster = new Broadcaster(); + private DCRemovalFilter_RB mDCRemovalFilter; + private AutomaticFrequencyControl_RB mAutomaticFrequencyControl; + private boolean mRemoveDC = true; + + private Listener mListener; + + /** + * FM Demodulator with integrated DC removal and automatic frequency + * correction control. + * + * @param pass - pass frequency for IQ filtering prior to demodulation. This + * frequency should be half of the signal bandwidth since the filter will + * be applied against each of the inphase and quadrature signals and the + * combined pass bandwidth will be twice this value. + * + * @param stop - stop frequency for IQ filtering prior to demodulation. + * + * @param maxFrequencyCorrection - defines the maximum frequency +/- that + * the correction controller can adjust from the center tuned frequency. + * Set this value to 0 for no frequency correction control. + */ + public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection ) + { + this( pass, stop, maxFrequencyCorrection, true ); + } + + public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection, boolean removeDC ) + { + assert( stop > pass ); + + mRemoveDC = removeDC; + + mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( + SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true ), 1.0f ); + + mDemodulator = new FMDemodulator_CB( 1.0f ); + mIQFilter.setListener( mDemodulator ); + + mUnfilteredBufferBroadcaster = new Broadcaster(); + mDemodulator.setListener( mUnfilteredBufferBroadcaster ); + + if( maxFrequencyCorrection > 0 ) + { + /* Frequency correction requires unfiltered samples in order to detect + * the DC component present in a mistuned FM demodulated sample stream */ + mAutomaticFrequencyControl = new AutomaticFrequencyControl_RB( maxFrequencyCorrection ); + mUnfilteredBufferBroadcaster.addListener( mAutomaticFrequencyControl ); + } + + if( mRemoveDC ) + { + mDCRemovalFilter = new AveragingDCRemovalFilter_RB( DC_REMOVAL_RATIO ); + mUnfilteredBufferBroadcaster.addListener( mDCRemovalFilter ); + } + } + + @Override + public boolean hasFrequencyCorrectionControl() + { + return mAutomaticFrequencyControl != null; + } + + @Override + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return mAutomaticFrequencyControl; + } + + @Override + public Listener getComplexBufferListener() + { + return mIQFilter; + } + + @Override + public void dispose() + { + mIQFilter.dispose(); + mIQFilter = null; + + mDemodulator.dispose(); + mDemodulator = null; + + mDCRemovalFilter.dispose(); + mDCRemovalFilter = null; + + if( mAutomaticFrequencyControl != null ) + { + mAutomaticFrequencyControl.dispose(); + mAutomaticFrequencyControl = null; + } + + if( mUnfilteredBufferBroadcaster != null ) + { + mUnfilteredBufferBroadcaster.dispose(); + mUnfilteredBufferBroadcaster = null; + } + } + + @Override + public void init() + { + mDemodulator.reset(); + + if( mDCRemovalFilter != null ) + { + mDCRemovalFilter.reset(); + } + + if( mAutomaticFrequencyControl != null ) + { + mAutomaticFrequencyControl.reset(); + } + } + + @Override + public void setRealBufferListener( Listener listener ) + { + mListener = listener; + + if( mRemoveDC ) + { + mDCRemovalFilter.setListener( listener ); + } + else + { + mUnfilteredBufferBroadcaster.addListener( listener ); + } + } + + @Override + public void removeRealBufferListener() + { + if( mRemoveDC ) + { + mDCRemovalFilter.removeListener(); + } + else + { + mUnfilteredBufferBroadcaster.removeListener( mListener ); + } + } +} diff --git a/src/module/record/ComplexBufferRecorderModule.java b/src/module/record/ComplexBufferRecorderModule.java new file mode 100644 index 00000000..2e896095 --- /dev/null +++ b/src/module/record/ComplexBufferRecorderModule.java @@ -0,0 +1,64 @@ +package module.record; + +import java.io.IOException; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.wave.ComplexWaveRecorder; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; + +public class ComplexBufferRecorderModule extends Module + implements IComplexBufferListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( ComplexBufferRecorderModule.class ); + + private ComplexWaveRecorder mRecorder; + private String mFilename; + + public ComplexBufferRecorderModule( String filename ) + { + mFilename = filename; + mRecorder = new ComplexWaveRecorder( 48000, mFilename ); + } + + @Override + public Listener getComplexBufferListener() + { + return mRecorder; + } + + @Override + public void dispose() + { + try + { + mRecorder.stop(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping the recorder", e ); + } + } + + @Override + public void init() + { + mLog.debug( "Starting the recorder [" + mFilename + "]" ); + + try + { + mRecorder.start(); + } + catch ( IOException e ) + { + mLog.error( "Error starting the recorder", e ); + } + } + +} diff --git a/src/module/record/RealBufferRecorderModule.java b/src/module/record/RealBufferRecorderModule.java new file mode 100644 index 00000000..a31bb981 --- /dev/null +++ b/src/module/record/RealBufferRecorderModule.java @@ -0,0 +1,64 @@ +package module.record; + +import java.io.IOException; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.wave.FloatWaveRecorder; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; + +public class RealBufferRecorderModule extends Module + implements IRealBufferListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( RealBufferRecorderModule.class ); + + private FloatWaveRecorder mRecorder; + private String mFilename; + + public RealBufferRecorderModule( String filename ) + { + mFilename = filename; + mRecorder = new FloatWaveRecorder( 48000, mFilename ); + } + + @Override + public Listener getRealBufferListener() + { + return mRecorder; + } + + @Override + public void dispose() + { + try + { + mRecorder.stop(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping the recorder", e ); + } + } + + @Override + public void init() + { + mLog.debug( "Starting the recorder [" + mFilename + "]" ); + + try + { + mRecorder.start(); + } + catch ( IOException e ) + { + mLog.error( "Error starting the recorder", e ); + } + } + +} diff --git a/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java b/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java index eed918b2..cb67d693 100644 --- a/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java +++ b/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java @@ -210,7 +210,7 @@ public abstract class AbstractTileFactory extends TileFactory @Override public Thread newThread(Runnable r) { - Thread t = new Thread(r, "tile-pool-" + count++); + Thread t = new Thread(r, "map tile fetcher-pool-" + count++); t.setPriority(Thread.MIN_PRIORITY); t.setDaemon(true); return t; diff --git a/src/playlist/Playlist.java b/src/playlist/Playlist.java index c44214d7..859f3968 100644 --- a/src/playlist/Playlist.java +++ b/src/playlist/Playlist.java @@ -22,7 +22,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import alias.AliasDirectory; -import controller.channel.ChannelMapList; +import controller.channel.map.ChannelMapList; import controller.system.SystemList; @XmlSeeAlso( { AliasDirectory.class, diff --git a/src/playlist/PlaylistNode.java b/src/playlist/PlaylistNode.java index 1f740a57..93fdc2a8 100644 --- a/src/playlist/PlaylistNode.java +++ b/src/playlist/PlaylistNode.java @@ -23,8 +23,8 @@ import alias.AliasDirectory; import alias.AliasDirectoryNode; import controller.BaseNode; import controller.EmptyEditor; -import controller.channel.ChannelMapList; -import controller.channel.ChannelMapListNode; +import controller.channel.map.ChannelMapList; +import controller.channel.map.ChannelMapListNode; import controller.system.SystemList; import controller.system.SystemListNode; diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java index 388fd5b7..0ce16c73 100644 --- a/src/record/RecorderManager.java +++ b/src/record/RecorderManager.java @@ -21,12 +21,12 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import module.ProcessingChainOld; import properties.SystemProperties; import record.config.RecordConfiguration; import record.wave.ComplexWaveRecorder; import record.wave.FloatWaveRecorder; import util.TimeStamp; -import controller.channel.ProcessingChain; public class RecorderManager { @@ -36,7 +36,7 @@ public class RecorderManager { } - public List getRecorder( ProcessingChain channel ) + public List getRecorder( ProcessingChainOld channel ) { /* Note: the file suffix (ie .wav) gets added by the recorder */ diff --git a/src/record/wave/ComplexWaveRecorder.java b/src/record/wave/ComplexWaveRecorder.java index 1c0336ef..efe94c21 100644 --- a/src/record/wave/ComplexWaveRecorder.java +++ b/src/record/wave/ComplexWaveRecorder.java @@ -19,6 +19,7 @@ package record.wave; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.concurrent.LinkedBlockingQueue; import javax.sound.sampled.AudioFormat; @@ -29,15 +30,14 @@ import org.slf4j.LoggerFactory; import record.Recorder; import record.RecorderType; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.ComplexBuffer; import util.waveaudio.WaveWriter; -import buffer.ComplexSampleBufferAssembler; /** * Threaded WAVE audio recorder for recording complex (I&Q) samples to a wave * file */ -public class ComplexWaveRecorder extends Recorder implements Listener +public class ComplexWaveRecorder extends Recorder implements Listener { private final static Logger mLog = LoggerFactory.getLogger( ComplexWaveRecorder.class ); @@ -48,11 +48,12 @@ public class ComplexWaveRecorder extends Recorder implements Listener mListener; //Sample buffer queue ... constrained to 512 sample buffers (arbitrary size) private LinkedBlockingQueue mToWriteBufferQueue = new LinkedBlockingQueue( 512 ); - private ComplexSampleBufferAssembler mCurrentBuffer; public ComplexWaveRecorder( int sampleRate, String filename ) { @@ -64,11 +65,28 @@ public class ComplexWaveRecorder extends Recorder implements Listener listener ) + { + mListener = listener; + } public class BufferProcessor extends Thread { @@ -175,5 +196,4 @@ public class ComplexWaveRecorder extends Recorder implements Listener { private final static Logger mLog = LoggerFactory.getLogger( FloatWaveRecorder.class ); @@ -52,6 +57,8 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener new LinkedBlockingQueue( 512 ); private FloatSampleBufferAssembler mCurrentBuffer; + private Listener mListener; + public FloatWaveRecorder( int sampleRate, String filename ) { super( RecorderType.AUDIO ); @@ -67,6 +74,11 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener mFileName = filename; } + public void setListener( Listener listener ) + { + mListener = listener; + } + @Override public String getFileName() { @@ -127,7 +139,42 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener } } } - + + @Override + public void receive( RealBuffer buffer ) + { + //Dispatch the buffer to be written to disk + if( !mToWriteBufferQueue.offer( convert( buffer ) ) ) + { + //We had an overflow ... log it + mLog.error( "buffer overflow - throwing away a buffer of sample data" ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + /** + * Converts the float samples in a complex buffer to a little endian 16-bit + * buffer + */ + public static ByteBuffer convert( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + ByteBuffer converted = ByteBuffer.allocate( samples.length * 2 ); + converted.order( ByteOrder.LITTLE_ENDIAN ); + + for( float sample: samples ) + { + converted.putShort( (short)( sample * Short.MAX_VALUE ) ); + } + + return converted; + } + @Override public void receive( float sample ) { @@ -173,5 +220,4 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener } } } - } diff --git a/src/sample/SampleType.java b/src/sample/SampleType.java new file mode 100644 index 00000000..09e9196b --- /dev/null +++ b/src/sample/SampleType.java @@ -0,0 +1,8 @@ +package sample; + +public enum SampleType +{ + COMPLEX, //Baseband + REAL, //Demodulated + COMPLEX_AND_REAL; //Baseband and Demodulated +} diff --git a/src/sample/adapter/ComplexToFloatSampleConverter.java b/src/sample/adapter/ComplexToFloatSampleConverter.java index 8416fc88..2d90c878 100644 --- a/src/sample/adapter/ComplexToFloatSampleConverter.java +++ b/src/sample/adapter/ComplexToFloatSampleConverter.java @@ -20,14 +20,14 @@ package sample.adapter; import java.util.List; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.mixer.MixerChannel; /** * Receives complex samples and send either the left channel or the right * channel to the short sample listener */ -public class ComplexToFloatSampleConverter implements Listener> +public class ComplexToFloatSampleConverter implements Listener> { private MixerChannel mChannel; private Listener mListener; @@ -40,9 +40,9 @@ public class ComplexToFloatSampleConverter implements Listener samples ) + public void receive( List samples ) { - for( ComplexSample sample: samples ) + for( Complex sample: samples ) { mListener.receive( mChannel == MixerChannel.LEFT ? sample.left() : diff --git a/src/sample/adapter/FloatArrayToComplexAdapter.java b/src/sample/adapter/FloatArrayToComplexAdapter.java index 36e520f5..9a6abb41 100644 --- a/src/sample/adapter/FloatArrayToComplexAdapter.java +++ b/src/sample/adapter/FloatArrayToComplexAdapter.java @@ -18,11 +18,11 @@ package sample.adapter; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class FloatArrayToComplexAdapter implements Listener { - private Listener mListener; + private Listener mListener; @Override public void receive( Float[] samples ) @@ -35,7 +35,7 @@ public class FloatArrayToComplexAdapter implements Listener samples[ x ] != null && samples[ x + 1 ] != null ) { - mListener.receive( new ComplexSample( samples[ x ], + mListener.receive( new Complex( samples[ x ], samples[ x + 1 ] ) ); } } @@ -44,12 +44,12 @@ public class FloatArrayToComplexAdapter implements Listener } } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/sample/complex/ComplexSample.java b/src/sample/complex/Complex.java similarity index 58% rename from src/sample/complex/ComplexSample.java rename to src/sample/complex/Complex.java index 7b4bd6a9..0a061ac1 100644 --- a/src/sample/complex/ComplexSample.java +++ b/src/sample/complex/Complex.java @@ -23,33 +23,48 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * ComplexSample for handling left/right channel audio, or inphase/quadrature - * samples, etc. + * Complex sample and related utility methods */ -public class ComplexSample implements Serializable +public class Complex implements Serializable { private final static Logger mLog = - LoggerFactory.getLogger( ComplexSample.class ); + LoggerFactory.getLogger( Complex.class ); private static final long serialVersionUID = 1L; private float mLeft; private float mRight; - public ComplexSample( float left, float right ) + public Complex( float left, float right ) { mLeft = left; mRight = right; } - public ComplexSample() + public Complex() { this( 0.0f, 0.0f ); } - - public ComplexSample copy() + + public void setInphase( float inphase ) { - return new ComplexSample( mLeft, mRight ); + mLeft = inphase; + } + + public void setQuadrature( float quadrature ) + { + mRight = quadrature; + } + + public void setValues( float inphase, float quadrature ) + { + mLeft = inphase; + mRight = quadrature; + } + + public Complex copy() + { + return new Complex( mLeft, mRight ); } public String toString() @@ -67,9 +82,9 @@ public class ComplexSample implements Serializable /** * Returns a new sample representing the conjugate of this one */ - public ComplexSample conjugate() + public Complex conjugate() { - return new ComplexSample( mLeft, -mRight ); + return new Complex( mLeft, -mRight ); } /** @@ -81,22 +96,37 @@ public class ComplexSample implements Serializable mRight *= scalor; } - public static ComplexSample multiply( ComplexSample sample, float scalor ) + public static Complex multiply( Complex sample, float scalor ) { - return new ComplexSample( sample.left() * scalor, + return new Complex( sample.left() * scalor, sample.right() * scalor ); } + /** + * Calculates the inphase component of multiplying two complex numbers, A and B. + */ + public static float multiplyInphase( float inphaseA, float quadratureA, float inphaseB, float quadratureB ) + { + return ( inphaseA * inphaseB ) - ( quadratureA * quadratureB ); + } + + /** + * Calculates the quadrature component of multiplying two complex numbers, A and B. + */ + public static float multiplyQuadrature( float inphaseA, float quadratureA, float inphaseB, float quadratureB ) + { + return ( quadratureA * inphaseB ) + ( inphaseA * quadratureB ); + } + /** * Multiplies this sample by the multiplier sample */ - public void multiply( ComplexSample multiplier ) + public void multiply( Complex multiplier ) { - float inphase = ( inphase() * multiplier.inphase() ) - - ( quadrature() * multiplier.quadrature() ); - - float quadrature = ( quadrature() * multiplier.inphase() ) + - ( inphase() * multiplier.quadrature() ); + float inphase = multiplyInphase( inphase(), quadrature(), + multiplier.inphase(), multiplier.quadrature() ); + float quadrature = multiplyQuadrature( inphase(), quadrature(), + multiplier.inphase(), multiplier.quadrature() ); mLeft = inphase; mRight = quadrature; @@ -105,31 +135,47 @@ public class ComplexSample implements Serializable /** * Multiplies both samples returning a new sample with the results */ - public static ComplexSample multiply( ComplexSample sample1, - ComplexSample sample2 ) + public static Complex multiply( Complex sample1, Complex sample2 ) { - float inphase = ( sample1.inphase() * sample2.inphase() ) - - ( sample1.quadrature() * sample2.quadrature() ); + float inphase = multiplyInphase( sample1.inphase(), sample1.quadrature(), + sample2.inphase(), sample2.quadrature() ); + float quadrature = multiplyQuadrature( sample1.inphase(), sample1.quadrature(), + sample2.inphase(), sample2.quadrature() ); - float quadrature = ( sample1.quadrature() * sample2.inphase() ) + - ( sample1.inphase() * sample2.quadrature() ); - - return new ComplexSample( inphase, quadrature ); + return new Complex( inphase, quadrature ); } - public static ComplexSample multiply( ComplexSample sample, + public static Complex multiply( Complex sample, Float inphase, Float quadrature ) { - float i = ( sample.inphase() * inphase ) - ( sample.quadrature() * quadrature ); - float q = ( sample.quadrature() * inphase ) + ( sample.inphase() * quadrature ); + float i = multiplyInphase( sample.inphase(), sample.quadrature(), + inphase, quadrature ); + float q = multiplyQuadrature( sample.inphase(), sample.quadrature(), + inphase, quadrature ); - return new ComplexSample( i, q ); + return new Complex( i, q ); + } + + public static Complex multiply( Float inphase, Float quadrature, Complex sample ) + { + float i = multiplyInphase( inphase, quadrature, sample.inphase(), sample.quadrature() ); + float q = multiplyQuadrature( inphase, quadrature, sample.inphase(), sample.quadrature() ); + + return new Complex( i, q ); } + public static Complex multiply( float inphaseA, float quadratureA, float inphaseB, float quadratureB ) + { + float i = multiplyInphase( inphaseA, quadratureA, inphaseB, quadratureB ); + float q = multiplyQuadrature( inphaseA, quadratureA, inphaseB, quadratureB ); + + return new Complex( i, q ); + } + /** * Adds the adder sample value to this sample */ - public void add( ComplexSample adder ) + public void add( Complex adder ) { mLeft += adder.left(); mRight += adder.right(); @@ -138,18 +184,18 @@ public class ComplexSample implements Serializable /** * Adds the two complex samples returning a new complex sample with the result */ - public static ComplexSample add( ComplexSample first, ComplexSample second ) + public static Complex add( Complex first, Complex second ) { - return new ComplexSample( first.left() + second.left(), + return new Complex( first.left() + second.left(), first.right() + second.right() ); } /** * Adds the two complex samples returning a new complex sample with the result */ - public static ComplexSample subtract( ComplexSample first, ComplexSample second ) + public static Complex subtract( Complex first, Complex second ) { - return new ComplexSample( first.left() - second.left(), + return new Complex( first.left() - second.left(), first.right() - second.right() ); } @@ -160,6 +206,11 @@ public class ComplexSample implements Serializable { return (float)Math.sqrt( magnitudeSquared() ); } + + public static float magnitude( float inphase, float quadrature ) + { + return (float)Math.sqrt( ( inphase * inphase ) + ( quadrature * quadrature ) ); + } /** * Magnitude squared of this sample @@ -293,9 +344,9 @@ public class ComplexSample implements Serializable * @param angle in radians * @return */ - public static ComplexSample fromAngle( float angle ) + public static Complex fromAngle( float angle ) { - return new ComplexSample( (float)Math.cos( angle ), + return new Complex( (float)Math.cos( angle ), (float)Math.sin( angle ) ); } @@ -346,16 +397,21 @@ public class ComplexSample implements Serializable */ public float envelope() { - float realAbsolute = realAbsolute(); - float imagAbsolute = imaginaryAbsolute(); + return envelope( mLeft, mRight ); + } + + public static float envelope( float inphase, float quadrature ) + { + float inphaseAbsolute = Math.abs( inphase ); + float quadratureAbsolute = Math.abs( quadrature ); - if( realAbsolute > imagAbsolute ) + if( inphaseAbsolute > quadratureAbsolute ) { - return realAbsolute + ( 0.4f * imagAbsolute ); + return inphaseAbsolute + ( 0.4f * quadratureAbsolute ); } else { - return imagAbsolute + ( 0.4f * realAbsolute ); + return quadratureAbsolute + ( 0.4f * inphaseAbsolute ); } } @@ -363,11 +419,11 @@ public class ComplexSample implements Serializable { double angle = Math.PI; //180 degrees - ComplexSample s1 = new ComplexSample( (float)Math.sin( angle ), (float)Math.cos( angle ) ); + Complex s1 = new Complex( (float)Math.sin( angle ), (float)Math.cos( angle ) ); - ComplexSample tap = new ComplexSample( 1.0f, -0.1f ); + Complex tap = new Complex( 1.0f, -0.1f ); - ComplexSample convolved = ComplexSample.multiply( s1, tap ); + Complex convolved = Complex.multiply( s1, tap ); mLog.debug( "s: " + s1.toString() + " t: " + tap.toString() + diff --git a/src/sample/complex/ComplexBuffer.java b/src/sample/complex/ComplexBuffer.java index 6aa3b37d..9a3a4b66 100644 --- a/src/sample/complex/ComplexBuffer.java +++ b/src/sample/complex/ComplexBuffer.java @@ -1,6 +1,8 @@ package sample.complex; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; public class ComplexBuffer { @@ -32,6 +34,21 @@ public class ComplexBuffer return mSamples; } + /** + * Converts this sample buffer into a list of complex samples + */ + public List getComplexSamples() + { + List samples = new ArrayList(); + + for( int x = 0; x < mSamples.length; x += 2 ) + { + samples.add( new Complex( mSamples[ x ], mSamples[ x + 1 ] ) ); + } + + return samples; + } + /** * Cleanup method to nullify all data and references */ diff --git a/src/sample/complex/ComplexBufferToStreamConverter.java b/src/sample/complex/ComplexBufferToStreamConverter.java new file mode 100644 index 00000000..a4809c2b --- /dev/null +++ b/src/sample/complex/ComplexBufferToStreamConverter.java @@ -0,0 +1,30 @@ +package sample.complex; + +import sample.Listener; + +/** + * Real sample buffer to stream converter + */ +public class ComplexBufferToStreamConverter implements Listener +{ + private ComplexSampleListener mListener; + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + mListener.receive( samples[ x ], samples[ x + 1 ] ); + } + } + } + + public void setListener( ComplexSampleListener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/complex/ComplexSampleAssembler.java b/src/sample/complex/ComplexSampleAssembler.java index ab30b868..f7872160 100644 --- a/src/sample/complex/ComplexSampleAssembler.java +++ b/src/sample/complex/ComplexSampleAssembler.java @@ -23,17 +23,17 @@ import java.util.List; import sample.Listener; import sample.Provider; -public class ComplexSampleAssembler implements Listener, - Provider> +public class ComplexSampleAssembler implements Listener, + Provider> { private ArrayList mBuffer; private int mBufferSize; - private Listener> mListener; + private Listener> mListener; public ComplexSampleAssembler( int bufferSize ) { mBufferSize = bufferSize; - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); } public void dispose() @@ -43,7 +43,7 @@ public class ComplexSampleAssembler implements Listener, } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer.add( sample ); @@ -59,13 +59,13 @@ public class ComplexSampleAssembler implements Listener, } @Override - public void setListener( Listener> listener ) + public void setListener( Listener> listener ) { mListener = listener; } @Override - public void removeListener( Listener> listener ) + public void removeListener( Listener> listener ) { mListener = null; } diff --git a/src/sample/complex/ComplexSampleListListener.java b/src/sample/complex/ComplexSampleListListener.java index ed2cc1ea..564ff798 100644 --- a/src/sample/complex/ComplexSampleListListener.java +++ b/src/sample/complex/ComplexSampleListListener.java @@ -21,5 +21,5 @@ import java.util.List; public interface ComplexSampleListListener { - public void receive( List samples ); + public void receive( List samples ); } diff --git a/src/sample/complex/ComplexSampleUtils.java b/src/sample/complex/ComplexSampleUtils.java index ba95a39b..a2bd93eb 100644 --- a/src/sample/complex/ComplexSampleUtils.java +++ b/src/sample/complex/ComplexSampleUtils.java @@ -21,26 +21,26 @@ package sample.complex; public class ComplexSampleUtils { - public static ComplexSample multiply( ComplexSample a, - ComplexSample b ) + public static Complex multiply( Complex a, + Complex b ) { - return new ComplexSample( + return new Complex( ( a.inphase() * b.inphase() ) - ( a.quadrature() * b.quadrature() ), ( ( a.quadrature() * b.inphase() ) + ( a.inphase() * b.quadrature() ) ) ); } - public static ComplexSample minus( ComplexSample a, ComplexSample b ) + public static Complex minus( Complex a, Complex b ) { - return new ComplexSample( a.inphase() - b.inphase(), + return new Complex( a.inphase() - b.inphase(), a.quadrature() - b.quadrature() ); } - public static double magnitude( ComplexSample sample ) + public static double magnitude( Complex sample ) { return Math.sqrt( magnitudeSquared( sample ) ); } - public static int magnitudeSquared( ComplexSample sample ) + public static int magnitudeSquared( Complex sample ) { return (int)( ( sample.inphase() * sample.inphase() ) + ( sample.quadrature() * sample.quadrature() ) ); diff --git a/src/sample/complex/ComplexToComplexBufferAssembler.java b/src/sample/complex/ComplexToComplexBufferAssembler.java new file mode 100644 index 00000000..28c032f6 --- /dev/null +++ b/src/sample/complex/ComplexToComplexBufferAssembler.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package sample.complex; + +import sample.Listener; + +/** + * Assembles complex float samples into a ComplexBuffer containing an array of floats + */ +public class ComplexToComplexBufferAssembler implements ComplexSampleListener +{ + private int mBufferSize; + private float[] mBuffer; + private int mBufferPointer; + + private Listener mListener; + + public ComplexToComplexBufferAssembler( int bufferSize ) + { + assert( bufferSize % 2 == 0 ); + + mBufferSize = bufferSize; + mBuffer = new float[ mBufferSize ]; + } + + public void dispose() + { + mListener = null; + mBuffer = null; + } + + public void reset() + { + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + + @Override + public void receive( float i, float q ) + { + mBuffer[ mBufferPointer ] = i; + + mBufferPointer++; + + mBuffer[ mBufferPointer ] = q; + + mBufferPointer++; + + if( mBufferPointer >= mBufferSize ) + { + if( mListener != null ) + { + mListener.receive( new ComplexBuffer( mBuffer ) ); + } + + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/complex/IComplexBufferListener.java b/src/sample/complex/IComplexBufferListener.java new file mode 100644 index 00000000..5ec64dbb --- /dev/null +++ b/src/sample/complex/IComplexBufferListener.java @@ -0,0 +1,8 @@ +package sample.complex; + +import sample.Listener; + +public interface IComplexBufferListener +{ + public Listener getComplexBufferListener(); +} diff --git a/src/sample/complex/RateCounter_CB.java b/src/sample/complex/RateCounter_CB.java new file mode 100644 index 00000000..abdc35e5 --- /dev/null +++ b/src/sample/complex/RateCounter_CB.java @@ -0,0 +1,56 @@ +package sample.complex; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; + +public class RateCounter_CB implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( RateCounter_CB.class ); + + private Listener mListener; + private long mCount; + private long mStartTime = -1; + + private String mName; + + public RateCounter_CB() + { + mName = "UNNAMED"; + } + + public RateCounter_CB( String name ) + { + mName = name; + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mStartTime == -1 ) + { + mStartTime = System.currentTimeMillis(); + } + + mCount += ( buffer.getSamples().length / 2 ); + + long elapsed = System.currentTimeMillis() - mStartTime; + + if( elapsed != 0 ) + { + mLog.debug( "[" + mName + "] Sample Rate: " + ( mCount / elapsed ) + " kHz" ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/complex/ThreadKiller.java b/src/sample/complex/ThreadKiller.java index 3d695332..a6928865 100644 --- a/src/sample/complex/ThreadKiller.java +++ b/src/sample/complex/ThreadKiller.java @@ -22,7 +22,7 @@ package sample.complex; * ComplexSample objects, send this class to the thread as a sentinal value * to kill the processing thread. */ -public class ThreadKiller extends ComplexSample +public class ThreadKiller extends Complex { private static final long serialVersionUID = 1L; diff --git a/src/sample/real/IRealBufferListener.java b/src/sample/real/IRealBufferListener.java new file mode 100644 index 00000000..6ef33052 --- /dev/null +++ b/src/sample/real/IRealBufferListener.java @@ -0,0 +1,8 @@ +package sample.real; + +import sample.Listener; + +public interface IRealBufferListener +{ + public Listener getRealBufferListener(); +} diff --git a/src/sample/real/IRealBufferProvider.java b/src/sample/real/IRealBufferProvider.java new file mode 100644 index 00000000..0b11debd --- /dev/null +++ b/src/sample/real/IRealBufferProvider.java @@ -0,0 +1,9 @@ +package sample.real; + +import sample.Listener; + +public interface IRealBufferProvider +{ + public void setRealBufferListener( Listener listener ); + public void removeRealBufferListener(); +} diff --git a/src/sample/real/RateCounter_RB.java b/src/sample/real/RateCounter_RB.java new file mode 100644 index 00000000..de3ca3d7 --- /dev/null +++ b/src/sample/real/RateCounter_RB.java @@ -0,0 +1,56 @@ +package sample.real; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; + +public class RateCounter_RB implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( RateCounter_RB.class ); + + private Listener mListener; + private long mCount; + private long mStartTime = -1; + + private String mName; + + public RateCounter_RB() + { + mName = "UNNAMED"; + } + + public RateCounter_RB( String name ) + { + mName = name; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mStartTime == -1 ) + { + mStartTime = System.currentTimeMillis(); + } + + mCount += buffer.getSamples().length; + + long elapsed = System.currentTimeMillis() - mStartTime; + + if( elapsed != 0 ) + { + mLog.debug( "[" + mName + "] Sample Rate: " + ( mCount / elapsed ) + " kHz" ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/real/RealBufferToStreamConverter.java b/src/sample/real/RealBufferToStreamConverter.java new file mode 100644 index 00000000..0758fb20 --- /dev/null +++ b/src/sample/real/RealBufferToStreamConverter.java @@ -0,0 +1,28 @@ +package sample.real; + +import sample.Listener; + +/** + * Real sample buffer to stream converter + */ +public class RealBufferToStreamConverter implements Listener +{ + private RealSampleListener mListener; + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + for( float sample: buffer.getSamples() ) + { + mListener.receive( sample ); + } + } + } + + public void setListener( RealSampleListener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/real/RealToRealBufferAssembler.java b/src/sample/real/RealToRealBufferAssembler.java new file mode 100644 index 00000000..de12e4de --- /dev/null +++ b/src/sample/real/RealToRealBufferAssembler.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package sample.real; + +import sample.Listener; + +/** + * Assembles single float samples into a RealBuffer containing an array of floats + */ +public class RealToRealBufferAssembler implements RealSampleListener +{ + private int mBufferSize; + private float[] mBuffer; + private int mBufferPointer; + + private Listener mListener; + + public RealToRealBufferAssembler( int bufferSize ) + { + mBufferSize = bufferSize; + mBuffer = new float[ mBufferSize ]; + } + + public void dispose() + { + mListener = null; + mBuffer = null; + } + + public void reset() + { + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + + @Override + public void receive( float sample ) + { + mBuffer[ mBufferPointer ] = sample; + + mBufferPointer++; + + if( mBufferPointer >= mBufferSize ) + { + if( mListener != null ) + { + mListener.receive( new RealBuffer( mBuffer ) ); + } + + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/source/ComplexSource.java b/src/source/ComplexSource.java index c1b697b7..eeb4084e 100644 --- a/src/source/ComplexSource.java +++ b/src/source/ComplexSource.java @@ -1,12 +1,10 @@ package source; -import java.util.List; - import sample.Provider; -import sample.complex.ComplexSample; +import sample.SampleType; +import sample.complex.ComplexBuffer; -public abstract class ComplexSource extends Source - implements Provider> +public abstract class ComplexSource extends Source implements Provider { public ComplexSource( String name ) { diff --git a/src/source/FloatSource.java b/src/source/FloatSource.java index a4d297a9..c8161a9c 100644 --- a/src/source/FloatSource.java +++ b/src/source/FloatSource.java @@ -1,6 +1,7 @@ package source; import sample.Provider; +import sample.SampleType; public abstract class FloatSource extends Source implements Provider { diff --git a/src/source/RealSource.java b/src/source/RealSource.java index 352ff26f..6b09ccd5 100644 --- a/src/source/RealSource.java +++ b/src/source/RealSource.java @@ -1,6 +1,7 @@ package source; import sample.Provider; +import sample.SampleType; import sample.real.RealBuffer; diff --git a/src/source/SampleProvider.java b/src/source/SampleProvider.java index d9af4796..3cc84749 100644 --- a/src/source/SampleProvider.java +++ b/src/source/SampleProvider.java @@ -17,7 +17,7 @@ ******************************************************************************/ package source; -import source.Source.SampleType; +import sample.SampleType; public interface SampleProvider { diff --git a/src/source/Source.java b/src/source/Source.java index 506955d0..064cc6ce 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -17,6 +17,8 @@ ******************************************************************************/ package source; +import sample.SampleType; + /** * Abstract class to define the minimum functionality of a sample data provider. */ @@ -25,8 +27,6 @@ public abstract class Source protected String mName; protected SampleType mSampleType; - public enum SampleType { COMPLEX, REAL } - public Source( String name, SampleType sampleType ) { mName = name; diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java index 19d60a9c..0cf4f186 100644 --- a/src/source/SourceComponentEditor.java +++ b/src/source/SourceComponentEditor.java @@ -23,13 +23,13 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; +import net.miginfocom.swing.MigLayout; import source.config.SourceConfigFactory; import source.config.SourceConfiguration; -import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.ChannelNode; -import controller.channel.ChannelValidationException; public class SourceComponentEditor extends AbstractChannelEditor { @@ -42,7 +42,9 @@ public class SourceComponentEditor extends AbstractChannelEditor { super( channelNode ); - mComboSources = new JComboBox(); + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); + + mComboSources = new JComboBox(); mComboSources.setModel( new DefaultComboBoxModel( SourceType.values() ) ); mComboSources.addActionListener( new ActionListener() { @@ -86,6 +88,7 @@ public class SourceComponentEditor extends AbstractChannelEditor } }); + add( new JLabel( "Source:" ) ); add( mComboSources, "wrap" ); reset(); diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index 7febb994..8c783ab9 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -17,9 +17,9 @@ ******************************************************************************/ package source; +import source.config.SourceConfiguration; import source.mixer.MixerManager; import controller.ResourceManager; -import controller.channel.ProcessingChain; public class SourceManager { /** @@ -40,28 +40,25 @@ public class SourceManager { mResourceManager = resourceManager; } - public Source getSource( ProcessingChain channel ) throws SourceException + public Source getSource( SourceConfiguration config, int bandwidth ) + throws SourceException { Source retVal = null; - if( channel != null ) + switch( config.getSourceType() ) { - switch( channel.getChannel() - .getSourceConfiguration().getSourceType() ) - { - case MIXER: - retVal = MixerManager.getInstance().getSource( channel ); - break; - case TUNER: - retVal = mResourceManager.getTunerManager().getSource( channel ); - break; - case RECORDING: - retVal = mResourceManager.getRecordingSourceManager() - .getSource( channel ); - case NONE: - default: - break; - } + case MIXER: + retVal = MixerManager.getInstance().getSource( config ); + break; + case TUNER: + retVal = mResourceManager.getTunerManager().getSource( config, bandwidth ); + break; + case RECORDING: + retVal = mResourceManager.getRecordingSourceManager() + .getSource( config, bandwidth ); + case NONE: + default: + break; } return retVal; diff --git a/src/source/config/SourceConfigRecording.java b/src/source/config/SourceConfigRecording.java index 43204a2d..801fb211 100644 --- a/src/source/config/SourceConfigRecording.java +++ b/src/source/config/SourceConfigRecording.java @@ -20,6 +20,8 @@ package source.config; import javax.xml.bind.annotation.XmlAttribute; import source.SourceType; +import source.tuner.TunerChannel; +import source.tuner.TunerChannel.Type; public class SourceConfigRecording extends SourceConfiguration { @@ -58,4 +60,11 @@ public class SourceConfigRecording extends SourceConfiguration { return getRecordingAlias(); } + + public TunerChannel getTunerChannel() + { + /* Create a tuner channel with a default bandwidth of 15 kHz */ + return new TunerChannel( Type.LOCKED, mFrequency, 15000 ); + } + } diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java index ee4c5f54..61637d12 100644 --- a/src/source/config/SourceConfigTuner.java +++ b/src/source/config/SourceConfigTuner.java @@ -22,18 +22,29 @@ import java.text.DecimalFormat; import javax.xml.bind.annotation.XmlAttribute; import source.SourceType; +import source.tuner.TunerChannel; +import source.tuner.TunerChannel.Type; public class SourceConfigTuner extends SourceConfiguration { private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" ); private long mFrequency = 0; + private int mBandwidth = 15000; public SourceConfigTuner() { super( SourceType.TUNER ); } + public SourceConfigTuner( TunerChannel tunerChannel ) + { + this(); + + mFrequency = tunerChannel.getFrequency(); + mBandwidth = tunerChannel.getBandwidth(); + } + @XmlAttribute( name = "frequency" ) public long getFrequency() { @@ -50,4 +61,9 @@ public class SourceConfigTuner extends SourceConfiguration { return sFORMAT.format( (double)mFrequency / 1000000.0d ) + " MHz"; } + + public TunerChannel getTunerChannel() + { + return new TunerChannel( Type.LOCKED, mFrequency, mBandwidth ); + } } diff --git a/src/source/mixer/ComplexMixer.java b/src/source/mixer/ComplexMixer.java index 5a92d8ae..cb7a68ea 100644 --- a/src/source/mixer/ComplexMixer.java +++ b/src/source/mixer/ComplexMixer.java @@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.adapter.SampleAdapter; import sample.complex.ComplexBuffer; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.SourceException; public class ComplexMixer @@ -102,15 +102,15 @@ public class ComplexMixer mBufferReader.stop(); } - private List convert( ComplexBuffer sampleBuffer ) + private List convert( ComplexBuffer sampleBuffer ) { float[] samples = sampleBuffer.getSamples(); - ArrayList converted = new ArrayList(); + ArrayList converted = new ArrayList(); for( int x = 0; x < samples.length; x += 2 ) { - converted.add( new ComplexSample( samples[ x ], samples[ x + 1 ] ) ); + converted.add( new Complex( samples[ x ], samples[ x + 1 ] ) ); } sampleBuffer.dispose(); diff --git a/src/source/mixer/MixerManager.java b/src/source/mixer/MixerManager.java index d233776d..b3156568 100644 --- a/src/source/mixer/MixerManager.java +++ b/src/source/mixer/MixerManager.java @@ -31,6 +31,8 @@ import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.TargetDataLine; +import module.ProcessingChainOld; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +42,6 @@ import source.config.SourceConfigMixer; import source.config.SourceConfiguration; import source.tuner.MixerTunerDataLine; import source.tuner.MixerTunerType; -import controller.channel.ProcessingChain; public class MixerManager { @@ -82,13 +83,10 @@ public class MixerManager return sInstance; } - public RealMixerSource getSource( ProcessingChain processingChain ) + public RealMixerSource getSource( SourceConfiguration config ) { RealMixerSource retVal = null; - SourceConfiguration config = - processingChain.getChannel().getSourceConfiguration(); - if( config instanceof SourceConfigMixer ) { SourceConfigMixer mixerConfig = (SourceConfigMixer)config; diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 2e9ad4bd..2de626eb 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -29,7 +29,7 @@ import javax.sound.sampled.TargetDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dsp.gain.RealAutomaticGainControl; +import dsp.gain.AutomaticGainControl; import sample.Listener; import sample.adapter.SampleAdapter; import sample.real.RealBuffer; @@ -50,7 +50,7 @@ public class RealMixerSource extends RealSource private String mDisplayName; private int mBytesPerFrame = 0; private SampleAdapter mSampleAdapter; - private RealAutomaticGainControl mAGC = new RealAutomaticGainControl(); + private AutomaticGainControl mAGC = new AutomaticGainControl(); CopyOnWriteArrayList> mSampleListeners = new CopyOnWriteArrayList>(); diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index c0de2b69..63c03d8a 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -17,8 +17,6 @@ ******************************************************************************/ package source.recording; -import gui.SDRTrunk; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -30,10 +28,10 @@ import settings.SettingsManager; import source.Source; import source.SourceException; import source.config.SourceConfigRecording; +import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import controller.ResourceManager; -import controller.channel.ProcessingChain; public class RecordingSourceManager { @@ -55,21 +53,21 @@ public class RecordingSourceManager * Iterates current recordings to get a tuner channel source for the frequency * specified in the channel config's source config object */ - public Source getSource( ProcessingChain processingChain ) + public Source getSource( SourceConfiguration config, int bandwidth ) throws SourceException { TunerChannelSource retVal = null; - if( processingChain.getChannel().getSourceConfiguration() - instanceof SourceConfigRecording ) + if( config instanceof SourceConfigRecording ) { - TunerChannel tunerChannel = - processingChain.getChannel().getTunerChannel(); + SourceConfigRecording configRecording = (SourceConfigRecording)config; + + TunerChannel tunerChannel = configRecording.getTunerChannel(); + + tunerChannel.setBandwidth( bandwidth ); - SourceConfigRecording config = (SourceConfigRecording)processingChain - .getChannel().getSourceConfiguration(); - - Recording recording = getRecordingFromAlias( config.getRecordingAlias() ); + Recording recording = getRecordingFromAlias( + configRecording.getRecordingAlias() ); if( recording != null ) { @@ -80,7 +78,7 @@ public class RecordingSourceManager else { throw new SourceException( "Recording with alias name [" + - config.getRecordingAlias() + "] is not currently available" ); + configRecording.getRecordingAlias() + "] is not currently available" ); } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index f2c1a95f..775c6c43 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -23,25 +23,25 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; +import sample.complex.Complex; import sample.complex.ComplexBuffer; -import sample.complex.ComplexSample; -import sample.complex.ComplexSampleAssembler; import source.ComplexSource; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeListener; import util.Oscillator; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; -import dsp.filter.ComplexPrimeCICDecimate; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; +import dsp.filter.cic.ComplexPrimeCICDecimate; public class TunerChannelSource extends ComplexSource implements FrequencyChangeListener, @@ -57,17 +57,18 @@ public class TunerChannelSource extends ComplexSource new LinkedTransferQueue(); private Tuner mTuner; private TunerChannel mTunerChannel; - private Oscillator mSineWaveGenerator; + private Oscillator mMixer; private ComplexPrimeCICDecimate mDecimationFilter; - - private ComplexSampleAssembler mBufferAssembler = - new ComplexSampleAssembler( 3000 ); - private long mTunerFrequency = 0; - private int mTunerFrequencyError = 0; - private int mTunerSampleRate; + private Listener mListener; private FrequencyChangeListener mFrequencyChangeListener; private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mTaskHandle; + + private long mTunerFrequency = 0; + private int mTunerFrequencyError = 0; + private int mTunerSampleRate; + + private DecimationProcessor mDecimationProcessor = new DecimationProcessor(); public TunerChannelSource( ThreadPoolManager threadPoolManager, Tuner tuner, @@ -77,7 +78,6 @@ public class TunerChannelSource extends ComplexSource super( "Tuner Channel Source" ); mThreadPoolManager = threadPoolManager; - mTuner = tuner; mTuner.addListener( (FrequencyChangeListener)this ); @@ -89,7 +89,7 @@ public class TunerChannelSource extends ComplexSource /* Setup the frequency translator to the current source frequency */ long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency(); - mSineWaveGenerator = new Oscillator( frequencyOffset, mTuner.getSampleRate() ); + mMixer = new Oscillator( frequencyOffset, mTuner.getSampleRate() ); /* Fire a sample rate change event to get the decimation chain setup */ frequencyChanged( new FrequencyChangeEvent( @@ -97,7 +97,7 @@ public class TunerChannelSource extends ComplexSource /* Schedule the decimation task to run 50 times a second */ mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION, - new DecimationProcessor(), 20, TimeUnit.MILLISECONDS ); + mDecimationProcessor, 20, TimeUnit.MILLISECONDS ); /* Finally, register to receive samples from the tuner */ mTuner.addListener( (Listener)this ); @@ -106,31 +106,44 @@ public class TunerChannelSource extends ComplexSource @Override public void dispose() { - mFrequencyChangeListener = null; - - //Tell the tuner to release our resources + /* Tell the tuner to release/unregister our resources */ mTuner.removeListener( (FrequencyChangeListener)this ); mTuner.releaseChannel( this ); + mTuner = null; + mTunerChannel = null; + /* Flag the decimation processor to shutdown */ + if( mDecimationProcessor != null ) + { + mDecimationProcessor.dispose(); + } + + } + + /** + * Cleanup method to allow the decimation processor to perform final cleanup + * once it gracefully shuts itself down + */ + private void cleanup() + { if( mTaskHandle != null ) { mThreadPoolManager.cancel( mTaskHandle ); + mThreadPoolManager = null; + mTaskHandle = null; } - + + mBuffer.clear(); mBuffer = null; -// mDecimationFilter.dispose(); + mFrequencyChangeListener = null; + mListener = null; + + mMixer = null; + + mDecimationFilter.dispose(); mDecimationFilter = null; - - mBufferAssembler.dispose(); - - mTuner = null; - - mTunerChannel = null; - - mTunerChannel = null; - mTuner = null; } public Tuner getTuner() @@ -144,22 +157,31 @@ public class TunerChannelSource extends ComplexSource } @Override - public void receive( ComplexBuffer sampleArray ) + public void receive( ComplexBuffer buffer ) { - mBuffer.add( sampleArray ); + mBuffer.add( buffer ); + } + + public void setFrequencyChangeListener( FrequencyChangeListener listener ) + { + mFrequencyChangeListener = listener; } @Override - public void setListener( Listener> listener ) - { - mBufferAssembler.setListener( listener ); - } - + public void setListener( Listener listener ) + { + /* Get a pointer to the listener so that if we have to change the + * decimation filter, we can re-add the listener */ + mListener = listener; + + mDecimationFilter.setListener( listener ); + } + @Override - public void removeListener( Listener> listener ) - { - mBufferAssembler.removeListener( listener ); - } + public void removeListener( Listener listener ) + { + mDecimationFilter.removeListener(); + } @Override public void frequencyChanged( FrequencyChangeEvent event ) @@ -180,7 +202,7 @@ public class TunerChannelSource extends ComplexSource if( mTunerSampleRate != sampleRate ) { /* Set the oscillator to the new frequency */ - mSineWaveGenerator.setSampleRate( sampleRate ); + mMixer.setSampleRate( sampleRate ); /* Get new decimation filter */ mDecimationFilter = FilterFactory @@ -192,7 +214,7 @@ public class TunerChannelSource extends ComplexSource WindowType.HAMMING ); /* re-add the original output listener */ - mDecimationFilter.setListener( mBufferAssembler ); + mDecimationFilter.setListener( mListener ); mTunerSampleRate = sampleRate; } @@ -204,7 +226,7 @@ public class TunerChannelSource extends ComplexSource mTunerChannel.getFrequency() - mTunerFrequencyError; - mSineWaveGenerator.setFrequency( frequencyErrorOffset ); + mMixer.setFrequency( frequencyErrorOffset ); break; case FREQUENCY: long frequency = event.getValue().longValue(); @@ -215,7 +237,7 @@ public class TunerChannelSource extends ComplexSource long frequencyOffset = frequency - mTunerChannel.getFrequency(); - mSineWaveGenerator.setFrequency( frequencyOffset ); + mMixer.setFrequency( frequencyOffset ); mTunerFrequency = frequency; break; @@ -226,16 +248,27 @@ public class TunerChannelSource extends ComplexSource public int getSampleRate() throws SourceException { - return mTuner.getSampleRate(); + return CHANNEL_RATE; } public long getFrequency() throws SourceException { - return mTuner.getFrequency(); + return mTunerChannel.getFrequency(); } + /** + * Decimates an inbound buffer of I/Q samples from the source down to the + * standard 48000 channel sample rate + */ public class DecimationProcessor implements Runnable { + private AtomicBoolean mDispose = new AtomicBoolean( false ); + + public void dispose() + { + mDispose.set( true ); + } + @Override public void run() { @@ -244,38 +277,59 @@ public class TunerChannelSource extends ComplexSource * run the program out of memory */ try { - List sampleBuffers = - new ArrayList(); - - if( mBuffer != null ) + /* Check to see if we've been shutdown */ + if( mDispose.get() ) { - /* Limit to 4, so that after a garbage collect we don't - * induce wild swings as it tries to take tons of samples - * and process them, causing delays and buffer refill */ - mBuffer.drainTo( sampleBuffers, 4 ); - - for( ComplexBuffer buffer: sampleBuffers ) + cleanup(); + return; + } + else + { + List sampleBuffers = + new ArrayList(); + + if( mBuffer != null ) { - float[] samples = buffer.getSamples(); - float[] translated = new float[ samples.length ]; - - /* Perform frequency translation */ - for( int x = 0; x < samples.length; x += 2 ) + mBuffer.drainTo( sampleBuffers, 4 ); + + for( ComplexBuffer buffer: sampleBuffers ) { - ComplexSample multiplier = - mSineWaveGenerator.nextComplex(); - - translated[ x ] = ( samples[ x ] * multiplier.inphase() ) - - ( samples[ x + 1 ] * multiplier.quadrature() ); - - translated[ x + 1 ] = ( samples[ x + 1 ] * multiplier.inphase() ) + - ( samples[ x ] * multiplier.quadrature() ); + /* Check to see if we've been shutdown */ + if( mDispose.get() ) + { + cleanup(); + return; + } + else + { + float[] samples = buffer.getSamples(); + + /* We make a copy of the buffer so that we don't affect + * anyone else that is using the same buffer, like other + * channels or the spectral display */ + float[] translated = new float[ samples.length ]; + + /* Perform frequency translation */ + for( int x = 0; x < samples.length; x += 2 ) + { + mMixer.rotate(); + + translated[ x ] = Complex.multiplyInphase( + samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); + + translated[ x + 1 ] = Complex.multiplyQuadrature( + samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); + } + + if( mDecimationFilter != null ) + { + mDecimationFilter.receive( new ComplexBuffer( translated ) ); + } + } } - mDecimationFilter.receive( translated ); + sampleBuffers.clear(); } - - sampleBuffers.clear(); } } catch( Exception e ) @@ -284,9 +338,4 @@ public class TunerChannelSource extends ComplexSource } } } - - public void addListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListener = listener; - } } diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index c5cf2064..eb978ef5 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -156,9 +156,8 @@ public abstract class TunerController implements Tunable } public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - Tuner tuner, TunerChannel tunerChannel ) - throws RejectedExecutionException, - SourceException + Tuner tuner, TunerChannel tunerChannel ) + throws RejectedExecutionException, SourceException { TunerChannelSource source = null; diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index c069ccd3..f11fedc4 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -33,6 +33,7 @@ import org.usb4java.LibUsb; import source.Source; import source.SourceException; import source.config.SourceConfigTuner; +import source.config.SourceConfiguration; import source.mixer.MixerManager; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerController; @@ -44,7 +45,6 @@ import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; import source.tuner.rtl.r820t.R820TTunerController; import controller.ResourceManager; -import controller.channel.ProcessingChain; public class TunerManager { @@ -76,15 +76,17 @@ public class TunerManager * Iterates current tuners to get a tuner channel source for the frequency * specified in the channel config's source config object */ - public Source getSource( ProcessingChain processingChain ) + public Source getSource( SourceConfiguration config, int bandwidth ) { TunerChannelSource retVal = null; - - if( processingChain.getChannel().getSourceConfiguration() - instanceof SourceConfigTuner ) + if( config instanceof SourceConfigTuner ) { - TunerChannel tunerChannel = processingChain.getChannel().getTunerChannel(); + SourceConfigTuner tunerConfig = (SourceConfigTuner)config; + + TunerChannel tunerChannel = tunerConfig.getTunerChannel(); + + tunerChannel.setBandwidth( bandwidth ); Iterator it = mTuners.iterator(); @@ -358,7 +360,8 @@ public class TunerManager try { HackRFTunerController hackRFController = - new HackRFTunerController( device, descriptor ); + new HackRFTunerController( device, descriptor, + mResourceManager.getThreadPoolManager() ); hackRFController.init(); @@ -409,7 +412,8 @@ public class TunerManager try { E4KTunerController controller = - new E4KTunerController( device, deviceDescriptor ); + new E4KTunerController( device, deviceDescriptor, + mResourceManager.getThreadPoolManager() ); controller.init(); @@ -432,14 +436,12 @@ public class TunerManager + "controller - " + se.getLocalizedMessage() ); } case RAFAELMICRO_R820T: - mLog.debug( "attempting to construct R820T " - + "tuner controller" ); try { R820TTunerController controller = - new R820TTunerController( device, deviceDescriptor ); + new R820TTunerController( device, deviceDescriptor, + mResourceManager.getThreadPoolManager() ); - mLog.debug( "initializing R820T tuner controller" ); controller.init(); RTL2832Tuner rtlTuner = @@ -450,14 +452,8 @@ public class TunerManager if( config != null ) { - mLog.debug( "applying tuner config to R820T tuner" ); rtlTuner.apply( config ); } - else - { - mLog.debug( "R820T tuner config was " - + "null - not applied" ); - } return new TunerInitStatus( rtlTuner, "LOADED" ); } diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl.java b/src/source/tuner/frequency/AutomaticFrequencyControl_R.java similarity index 95% rename from src/source/tuner/frequency/AutomaticFrequencyControl.java rename to src/source/tuner/frequency/AutomaticFrequencyControl_R.java index b655a1d5..4c52f7ef 100644 --- a/src/source/tuner/frequency/AutomaticFrequencyControl.java +++ b/src/source/tuner/frequency/AutomaticFrequencyControl_R.java @@ -25,11 +25,11 @@ import sample.real.RealSampleListener; import source.tuner.frequency.FrequencyChangeEvent.Attribute; import buffer.FloatAveragingBuffer; -public class AutomaticFrequencyControl extends FrequencyCorrectionControl +public class AutomaticFrequencyControl_R extends FrequencyCorrectionControl implements RealSampleListener { private final static Logger mLog = - LoggerFactory.getLogger( AutomaticFrequencyControl.class ); + LoggerFactory.getLogger( AutomaticFrequencyControl_R.class ); private static float LARGE_ERROR = 0.300f; private static float MEDIUM_ERROR = 0.150f; @@ -64,7 +64,7 @@ public class AutomaticFrequencyControl extends FrequencyCorrectionControl * * @param maximum - maximum allowable (+/-) frequency correction value. */ - public AutomaticFrequencyControl( int maximum ) + public AutomaticFrequencyControl_R( int maximum ) { super( maximum ); } diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java b/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java new file mode 100644 index 00000000..e51225d3 --- /dev/null +++ b/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java @@ -0,0 +1,197 @@ +package source.tuner.frequency; + +/******************************************************************************* + * 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 + ******************************************************************************/ + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.RealBuffer; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; +import buffer.FloatAveragingBuffer; + +public class AutomaticFrequencyControl_RB extends FrequencyCorrectionControl + implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( AutomaticFrequencyControl_RB.class ); + + private static float LARGE_ERROR = 0.300f; + private static float MEDIUM_ERROR = 0.150f; + private static float SMALL_ERROR = 0.030f; + private static float FINE_ERROR = 0.015f; + + private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz + private static int MEDIUM_FREQUENCY_CORRECTION = 500; + private static int SMALL_FREQUENCY_CORRECTION = 100; + private static int FINE_FREQUENCY_CORRECTION = 50; + + private static int SAMPLE_THRESHOLD = 15000; + private Mode mMode = Mode.FAST; + + private FloatAveragingBuffer mBuffer = + new FloatAveragingBuffer( SAMPLE_THRESHOLD ); + + private int mSampleCounter = 0; + private int mSkipCounter = 0; + private int mSkipThreshold = 4; + private int mErrorCorrection = 0; + + public enum Mode + { + NORMAL,FAST; + } + + /** + * Automatic Frequency Control. Monitors DC bias present in an FM + * demodulated audio stream and issues automatic frequency corrections to + * maintain the DC bias close to zero. + * + * @param maximum - maximum allowable (+/-) frequency correction value. + */ + public AutomaticFrequencyControl_RB( int maximum ) + { + super( maximum ); + } + + public void dispose() + { + super.dispose(); + + mBuffer = null; + } + + /** + * Resets the error correction value to zero and broadcasts a change event + */ + public void reset() + { + super.reset(); + + mMode = Mode.FAST; + dispatch(); + } + + /** + * Broadcasts a frequency correction change event to the registered listener + */ + private void dispatch() + { + if( mListener != null ) + { + mListener.frequencyChanged( + new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR, + mErrorCorrection ) ); + } + } + + @Override + public void receive( RealBuffer t ) + { + float average = 0.0f; + + for( float sample: t.getSamples() ) + { + average = mBuffer.get( sample ); + } + + //TODO: fix this + +// update( average ); + } + + public void receive( float sample ) + { + if( mMode == Mode.FAST ) + { + float average = mBuffer.get( sample ); + + mSampleCounter++; + + if( mSampleCounter >= SAMPLE_THRESHOLD ) + { + mSampleCounter = 0; + + update( average ); + } + } + else + { + mSkipCounter++; + + if( mSkipCounter >= mSkipThreshold ) + { + mSkipCounter = 0; + + float average = mBuffer.get( sample ); + + mSampleCounter++; + + if( mSampleCounter >= SAMPLE_THRESHOLD ) + { + mSampleCounter = 0; + + update( average ); + } + } + } + } + + private void update( float average ) + { + mMode = Mode.NORMAL; + + if( average > LARGE_ERROR ) + { + adjust( -LARGE_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average > MEDIUM_ERROR ) + { + adjust( -MEDIUM_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average > SMALL_ERROR ) + { + adjust( -SMALL_FREQUENCY_CORRECTION ); + } + else if( average > FINE_ERROR ) + { + adjust( -FINE_FREQUENCY_CORRECTION ); + } + else if( average < -LARGE_ERROR ) + { + adjust( LARGE_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average < -MEDIUM_ERROR ) + { + adjust( MEDIUM_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average < -SMALL_ERROR ) + { + adjust( SMALL_FREQUENCY_CORRECTION ); + } + else if( average < -FINE_ERROR ) + { + adjust( FINE_FREQUENCY_CORRECTION ); + } + } +} diff --git a/src/source/tuner/frequency/FrequencyCorrectionControl.java b/src/source/tuner/frequency/FrequencyCorrectionControl.java index a2853c67..bf3f6a7c 100644 --- a/src/source/tuner/frequency/FrequencyCorrectionControl.java +++ b/src/source/tuner/frequency/FrequencyCorrectionControl.java @@ -59,7 +59,7 @@ public class FrequencyCorrectionControl implements FrequencyChangeListener /** * Registers a listener to receive frequency correction events */ - public void setListener( FrequencyChangeListener listener ) + public void setFrequencyChangeListener( FrequencyChangeListener listener ) { mListener = listener; } @@ -92,7 +92,7 @@ public class FrequencyCorrectionControl implements FrequencyChangeListener * Resets frequency correction value to zero and notifies any reset * listeners */ - protected void reset() + public void reset() { setErrorCorrection( 0, false ); diff --git a/src/source/tuner/frequency/IFrequencyCorrectionController.java b/src/source/tuner/frequency/IFrequencyCorrectionController.java new file mode 100644 index 00000000..e937bd4a --- /dev/null +++ b/src/source/tuner/frequency/IFrequencyCorrectionController.java @@ -0,0 +1,8 @@ +package source.tuner.frequency; + + +public interface IFrequencyCorrectionController +{ + public boolean hasFrequencyCorrectionControl(); + public FrequencyCorrectionControl getFrequencyCorrectionControl(); +} diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index 66461a43..27acd03e 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -58,9 +58,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -78,12 +76,15 @@ import org.usb4java.LibUsbException; import org.usb4java.Transfer; import org.usb4java.TransferCallback; +import sample.Broadcaster; import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerController; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; public class HackRFTunerController extends TunerController @@ -114,8 +115,7 @@ public class HackRFTunerController extends TunerController private LinkedTransferQueue mFilledBuffers = new LinkedTransferQueue(); - private CopyOnWriteArrayList> mSampleListeners = - new CopyOnWriteArrayList>(); + private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); private BufferProcessor mBufferProcessor = new BufferProcessor(); @@ -127,14 +127,16 @@ public class HackRFTunerController extends TunerController private Device mDevice; private DeviceDescriptor mDeviceDescriptor; private DeviceHandle mDeviceHandle; + private ThreadPoolManager mThreadPoolManager; - public HackRFTunerController( Device device, DeviceDescriptor descriptor ) - throws SourceException + public HackRFTunerController( Device device, DeviceDescriptor descriptor, + ThreadPoolManager threadPoolManager ) throws SourceException { super( MIN_FREQUENCY, MAX_FREQUENCY ); mDevice = device; mDeviceDescriptor = descriptor; + mThreadPoolManager = threadPoolManager; } public void init() throws SourceException @@ -870,7 +872,7 @@ public class HackRFTunerController extends TunerController */ public void addListener( Listener listener ) { - mSampleListeners.add( listener ); + mComplexBufferBroadcaster.addListener( listener ); if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) { @@ -890,51 +892,20 @@ public class HackRFTunerController extends TunerController */ public void removeListener( Listener listener ) { - mSampleListeners.remove( listener ); - - if( mSampleListeners.isEmpty() ) + mComplexBufferBroadcaster.removeListener( listener ); + + if( !mComplexBufferBroadcaster.hasListeners() ) { mBufferProcessor.stop(); } } - /** - * Dispatches the sample array to each registered listener. If there is more - * than one listener, they receive a copy of the samples. If there is only - * one listener, or the last listener, they receive the original sample array. - * - * This is to facilitate garbage collection of the array when the listener - * is done processing the samples. - */ - public void broadcast( ComplexBuffer samples ) - { - Iterator> it = mSampleListeners.iterator(); - - while( it.hasNext() ) - { - Listener next = it.next(); - - /* if this is the last (or only) listener, send the original - * buffer, otherwise send a copy of the buffer */ - if( it.hasNext() ) - { - next.receive( samples.copyOf() ); - } - else - { - next.receive( samples ); - } - } - } - /** * Buffer processing thread. Fetches samples from the HackRF Tuner and * dispatches them to all registered listeners */ public class BufferProcessor implements Runnable, TransferCallback { - private ScheduledExecutorService mExecutor = - Executors.newScheduledThreadPool( 1 ); private ScheduledFuture mSampleDispatcherTask; private CopyOnWriteArrayList mTransfers; private AtomicBoolean mRunning = new AtomicBoolean(); @@ -960,9 +931,9 @@ public class HackRFTunerController extends TunerController } } - mSampleDispatcherTask = mExecutor - .scheduleAtFixedRate( new BufferDispatcher(), - 0, 20, TimeUnit.MILLISECONDS ); + mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), + 20, TimeUnit.MILLISECONDS ); while( mRunning.get() ) { @@ -1125,7 +1096,7 @@ public class HackRFTunerController extends TunerController { float[] samples = mSampleAdapter.convert( buffer ); - broadcast( new ComplexBuffer( samples ) ); + mComplexBufferBroadcaster.broadcast( new ComplexBuffer( samples ) ); } } catch( Exception e ) diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index acffcde3..0266ee55 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -29,9 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -51,16 +49,20 @@ import org.usb4java.LibUsbException; import org.usb4java.Transfer; import org.usb4java.TransferCallback; +import sample.Broadcaster; import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; +import sample.complex.Complex; import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.TunerController; import source.tuner.TunerType; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import buffer.FloatAveragingBuffer; import controller.ResourceManager; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; public abstract class RTL2832TunerController extends TunerController { @@ -104,12 +106,11 @@ public abstract class RTL2832TunerController extends TunerController private SampleRate mSampleRate = DEFAULT_SAMPLE_RATE; - private BufferProcessor mBufferProcessor = new BufferProcessor(); + private BufferProcessor mBufferProcessor; private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); - private CopyOnWriteArrayList> mSampleListeners = - new CopyOnWriteArrayList>(); + private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private LinkedTransferQueue mFilledBuffers = new LinkedTransferQueue(); @@ -127,6 +128,7 @@ public abstract class RTL2832TunerController extends TunerController public int mBufferSize = 131072; protected Descriptor mDescriptor; + private ThreadPoolManager mThreadPoolManager; /** * Abstract tuner controller device. Use the static getTunerClass() method @@ -135,11 +137,14 @@ public abstract class RTL2832TunerController extends TunerController */ public RTL2832TunerController( Device device, DeviceDescriptor deviceDescriptor, + ThreadPoolManager threadPoolManager, long minTunableFrequency, long maxTunableFrequency ) throws SourceException { super( minTunableFrequency, maxTunableFrequency ); + mThreadPoolManager = threadPoolManager; + mDevice = device; mDeviceDescriptor = deviceDescriptor; } @@ -1345,12 +1350,15 @@ public abstract class RTL2832TunerController extends TunerController */ public void addListener( Listener listener ) { - mSampleListeners.add( listener ); + mComplexBufferBroadcaster.addListener( listener ); - if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) + if( mBufferProcessor == null ) + { + mBufferProcessor = new BufferProcessor( mThreadPoolManager ); + } + + if( !mBufferProcessor.isRunning() ) { - mBufferProcessor = new BufferProcessor(); - Thread thread = new Thread( mBufferProcessor ); thread.setDaemon( true ); thread.setName( "RTL2832 Sample Processor" ); @@ -1365,59 +1373,35 @@ public abstract class RTL2832TunerController extends TunerController */ public void removeListener( Listener listener ) { - mSampleListeners.remove( listener ); - - if( mSampleListeners.isEmpty() ) + mComplexBufferBroadcaster.removeListener( listener ); + + if( !mComplexBufferBroadcaster.hasListeners() ) { mBufferProcessor.stop(); } } - /** - * Dispatches float sample buffers to all registered listeners - */ - public void broadcast( ComplexBuffer buffer ) - { - Iterator> it = mSampleListeners.iterator(); - - while( it.hasNext() ) - { - Listener next = it.next(); - - /* if this is the last (or only) listener, send him the original - * buffer, otherwise send him a copy of the buffer */ - if( it.hasNext() ) - { - next.receive( buffer.copyOf() ); - } - else - { - next.receive( buffer ); - } - } - } - /** * Buffer processing thread. Fetches samples from the RTL2832 Tuner and * dispatches them to all registered listeners */ public class BufferProcessor implements Runnable, TransferCallback { - private ScheduledExecutorService mExecutor = - Executors.newScheduledThreadPool( 2 ); private ScheduledFuture mSampleDispatcherTask; - private ScheduledFuture mSampleRateCounterTask; private CopyOnWriteArrayList mTransfers; private AtomicBoolean mRunning = new AtomicBoolean(); + private ThreadPoolManager mThreadPoolManager; + public BufferProcessor( ThreadPoolManager manager ) + { + mThreadPoolManager = manager; + } + @Override public void run() { if( mRunning.compareAndSet( false, true ) ) { - mLog.debug( "rtl2832 [" + getUniqueID() + - "] - starting sample fetch thread" ); - try { setSampleRate( mSampleRate ); @@ -1445,16 +1429,17 @@ public abstract class RTL2832TunerController extends TunerController } } - mSampleDispatcherTask = mExecutor - .scheduleAtFixedRate( new BufferDispatcher(), - 0, 20, TimeUnit.MILLISECONDS ); - - mSampleRateMonitor = - new SampleRateMonitor( mSampleRate.getRate() ); - - mSampleRateCounterTask = mExecutor - .scheduleAtFixedRate( mSampleRateMonitor, - 10, 10, TimeUnit.SECONDS ); + try + { + mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), + 20, TimeUnit.MILLISECONDS ); + } + catch( NullPointerException npe ) + { + mLog.error( "NPE! = tpm null: " + ( mThreadPoolManager == null ), npe ); + } + while( mRunning.get() ) { @@ -1478,15 +1463,12 @@ public abstract class RTL2832TunerController extends TunerController { if( mRunning.compareAndSet( true, false ) ) { - mLog.debug( "rtl2832 [" + getUniqueID() + - "] - stopping sample fetch thread" ); - cancel(); if( mSampleDispatcherTask != null ) { - mSampleDispatcherTask.cancel( true ); - mSampleRateCounterTask.cancel( true ); + mThreadPoolManager.cancel( mSampleDispatcherTask ); + mSampleDispatcherTask = null; mFilledBuffers.clear(); } } @@ -1621,7 +1603,7 @@ public abstract class RTL2832TunerController extends TunerController { float[] samples = mSampleAdapter.convert( buffer ); - broadcast( new ComplexBuffer( samples ) ); + mComplexBufferBroadcaster.broadcast( new ComplexBuffer( samples ) ); } } catch( Exception e ) diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index 09577e01..b9689a85 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -34,8 +34,8 @@ import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.rtl.RTL2832TunerController; -import source.tuner.rtl.RTL2832TunerController.Descriptor; import controller.ResourceManager; +import controller.ThreadPoolManager; public class E4KTunerController extends RTL2832TunerController { @@ -111,10 +111,12 @@ public class E4KTunerController extends RTL2832TunerController private E4KTunerEditorPanel mEditor; public E4KTunerController( Device device, - DeviceDescriptor deviceDescriptor ) + DeviceDescriptor deviceDescriptor, + ThreadPoolManager threadPoolManager ) throws SourceException { - super( device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY ); + super( device, deviceDescriptor, threadPoolManager, + MIN_FREQUENCY, MAX_FREQUENCY ); } @Override diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java index c80af5ab..fe08d0c3 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java @@ -270,64 +270,64 @@ public class R820TTunerConfigurationPanel extends JPanel add( new JLabel( "Sample Rate:" ) ); add( mComboSampleRate, "growx,push" ); - /** - * Sample Rate Correction - */ - SpinnerModel sampleRateCorrectionModel = - new SpinnerNumberModel( 0, //initial value - -32768, //min - 32767, //max - 1 ); //step - - mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); - - JSpinner.NumberEditor sampleRateCorrectionEditor = - (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); - - DecimalFormat sampleRateCorrectionFormat = - sampleRateCorrectionEditor.getFormat(); - - sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); - - sampleRateCorrectionEditor.getTextField() - .setHorizontalAlignment( SwingConstants.CENTER ); - - mSampleRateCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final int value = ((SpinnerNumberModel)mSampleRateCorrection - .getModel()).getNumber().intValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setSampleRateFrequencyCorrection( value ); - } - catch ( SourceException | LibUsbException e1 ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply " - + "sample rate correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "sample rate correction value: " + value + - e1.getLocalizedMessage() ); - } - } - } ); - } - } ); - - add( mSampleRateCorrection ); - add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" ); +// /** +// * Sample Rate Correction +// */ +// SpinnerModel sampleRateCorrectionModel = +// new SpinnerNumberModel( 0, //initial value +// -32768, //min +// 32767, //max +// 1 ); //step +// +// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); +// +// JSpinner.NumberEditor sampleRateCorrectionEditor = +// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); +// +// DecimalFormat sampleRateCorrectionFormat = +// sampleRateCorrectionEditor.getFormat(); +// +// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); +// +// sampleRateCorrectionEditor.getTextField() +// .setHorizontalAlignment( SwingConstants.CENTER ); +// +// mSampleRateCorrection.addChangeListener( new ChangeListener() +// { +// @Override +// public void stateChanged( ChangeEvent e ) +// { +// final int value = ((SpinnerNumberModel)mSampleRateCorrection +// .getModel()).getNumber().intValue(); +// +// EventQueue.invokeLater( new Runnable() +// { +// @Override +// public void run() +// { +// try +// { +// mController.setSampleRateFrequencyCorrection( value ); +// } +// catch ( SourceException | LibUsbException e1 ) +// { +// JOptionPane.showMessageDialog( +// R820TTunerConfigurationPanel.this, +// "E4K Tuner Controller - couldn't apply " +// + "sample rate correction value: " + value + +// e1.getLocalizedMessage() ); +// +// mLog.error( "E4K Tuner Controller - couldn't apply " +// + "sample rate correction value: " + value + +// e1.getLocalizedMessage() ); +// } +// } +// } ); +// } +// } ); +// +// add( mSampleRateCorrection ); +// add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" ); /** * Gain Controls diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java index 375a79b2..4d8ec9ce 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/source/tuner/rtl/r820t/R820TTunerController.java @@ -38,6 +38,7 @@ import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.rtl.RTL2832TunerController; import controller.ResourceManager; +import controller.ThreadPoolManager; public class R820TTunerController extends RTL2832TunerController { @@ -73,10 +74,12 @@ public class R820TTunerController extends RTL2832TunerController private JPanel mEditor; public R820TTunerController( Device device, - DeviceDescriptor deviceDescriptor ) - throws SourceException + DeviceDescriptor deviceDescriptor, + ThreadPoolManager threadPoolManager ) + throws SourceException { - super( device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY ); + super( device, deviceDescriptor, threadPoolManager, + MIN_FREQUENCY, MAX_FREQUENCY ); } @Override @@ -250,21 +253,15 @@ public class R820TTunerController extends RTL2832TunerController try { - mLog.debug( "initializing RTL2832 tuner baseband" ); initBaseband( mDeviceHandle ); - mLog.debug( "enabling I2C repeater" ); enableI2CRepeater( mDeviceHandle, true ); boolean i2CRepeaterControl = false; - mLog.debug( "initializing R820T tuner" ); initTuner( i2CRepeaterControl ); - mLog.debug( "disabling I2C repeater" ); enableI2CRepeater( mDeviceHandle, false ); - - mLog.debug( "initializing RTL2832 tuner controller super class" ); } catch( UsbException e ) { diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index c3e3729e..8dfd8895 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -35,7 +35,8 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.SampleType; +import sample.complex.Complex; public class ComplexWaveSource extends WaveSource { @@ -56,7 +57,7 @@ public class ComplexWaveSource extends WaveSource private static int BUFFER_SAMPLE_SIZE = 2000; - private Broadcaster mBroadcaster = new Broadcaster(); + private Broadcaster mBroadcaster = new Broadcaster(); /** * Single channel (mono) wave file playback source with optional looping @@ -227,7 +228,7 @@ public class ComplexWaveSource extends WaveSource if( broadcast ) { - send( new ComplexSample( (float)i / 32767.0f, (float)q / 32767.0f ) ); + send( new Complex( (float)i / 32767.0f, (float)q / 32767.0f ) ); } success = true; @@ -278,7 +279,7 @@ public class ComplexWaveSource extends WaveSource /** * Broadcasts a sample to the registered listeners */ - private void send( ComplexSample sample ) + private void send( Complex sample ) { mBroadcaster.broadcast( sample ); } @@ -286,12 +287,12 @@ public class ComplexWaveSource extends WaveSource /** * Adds a new listener to receive samples as they are read from the wave file */ - public void addListener( Listener listener ) + public void addListener( Listener listener ) { mBroadcaster.addListener( listener ); } - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mBroadcaster.removeListener( listener ); } diff --git a/src/source/wave/FloatWaveSource.java b/src/source/wave/FloatWaveSource.java index fbec02fb..a660679d 100644 --- a/src/source/wave/FloatWaveSource.java +++ b/src/source/wave/FloatWaveSource.java @@ -29,6 +29,7 @@ import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; +import sample.SampleType; import sample.adapter.ShortToFloatMap; import sample.real.RealSampleListener; diff --git a/src/source/wave/WaveSource.java b/src/source/wave/WaveSource.java index 55956520..f3470d1c 100644 --- a/src/source/wave/WaveSource.java +++ b/src/source/wave/WaveSource.java @@ -20,6 +20,7 @@ package source.wave; import java.io.File; import java.io.IOException; +import sample.SampleType; import source.Source; public abstract class WaveSource extends Source diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index e2f1cc32..d57870fe 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -17,14 +17,15 @@ import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; import sample.Listener; +import sample.SampleType; import sample.complex.ComplexBuffer; +import sample.real.RealBuffer; import sample.real.RealSampleListener; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; -import source.Source.SampleType; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Attribute; import spectrum.converter.DFTResultsConverter; @@ -44,7 +45,7 @@ import dsp.filter.Window.WindowType; public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener, - RealSampleListener, + Listener, SettingChangeListener, SpectralDisplayAdjuster { @@ -179,7 +180,7 @@ public class ChannelSpectrumPanel extends JPanel start(); } break; - case CHANNEL_DELETED: + case CHANNEL_DISABLED: if( event.getChannel() == mCurrentChannel ) { if( mEnabled.get() ) @@ -190,7 +191,7 @@ public class ChannelSpectrumPanel extends JPanel mCurrentChannel = null; } break; - case CHANGE_ENABLED: + case CHANNEL_ENABLED: if( event.getChannel() == mCurrentChannel && !event.getChannel().isProcessing() ) { @@ -205,7 +206,7 @@ public class ChannelSpectrumPanel extends JPanel { if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() ) { - mCurrentChannel.getProcessingChain().addRealListener( (RealSampleListener)this ); + mCurrentChannel.getProcessingChain().addRealBufferListener( this ); mDFTProcessor.start(); } } @@ -215,7 +216,7 @@ public class ChannelSpectrumPanel extends JPanel if( mCurrentChannel != null && mCurrentChannel.isProcessing() ) { - mCurrentChannel.getProcessingChain().removeRealListener( (RealSampleListener)this ); + mCurrentChannel.getProcessingChain().removeRealBufferListener( this ); } mDFTProcessor.stop(); @@ -251,9 +252,12 @@ public class ChannelSpectrumPanel extends JPanel } @Override - public void receive( float sample ) + public void receive( RealBuffer buffer ) { - mDecimatingSampleAssembler.receive( sample ); + for( float sample: buffer.getSamples() ) + { + mDecimatingSampleAssembler.receive( sample ); + } } /** @@ -454,7 +458,7 @@ public class ChannelSpectrumPanel extends JPanel public class DecimatingSampleAssembler { private FloatHalfBandFilter mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002f ); private SampleAssembler mSampleAssembler; diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 6df2e493..4172d1e5 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -30,10 +30,10 @@ import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.NamingThreadFactory; import sample.Listener; +import sample.SampleType; import sample.complex.ComplexBuffer; -import source.Source; -import source.Source.SampleType; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeListener; import spectrum.converter.DFTResultsConverter; @@ -56,8 +56,8 @@ public class DFTProcessor implements Listener, private ArrayBlockingQueue mQueue = new ArrayBlockingQueue( 200 ); - private ScheduledExecutorService mScheduler = - Executors.newScheduledThreadPool(1); + private ScheduledExecutorService mScheduler = Executors + .newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); private FFTWidth mFFTWidth = FFTWidth.FFT04096; private FFTWidth mNewFFTWidth = FFTWidth.FFT04096; @@ -129,7 +129,7 @@ public class DFTProcessor implements Listener, setWindowType( mWindowType ); } - public Source.SampleType getSampleType() + public SampleType getSampleType() { return mSampleType; } @@ -177,7 +177,7 @@ public class DFTProcessor implements Listener, /** * Reset the scheduler */ - mScheduler = Executors.newScheduledThreadPool(1); + mScheduler = Executors.newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); /** * Schedule the DFT to run calculations at a fixed rate @@ -293,16 +293,25 @@ public class DFTProcessor implements Listener, getNextBuffer(); } - int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; - - while( remaining > 0 && samplesAvailable > 0 ) + /* If we don't have new samples to use, send the current frame with + * the remaining values as zero */ + if( mCurrentBuffer == null ) { - currentFrame[ currentFramePointer++ ] = - (float)mCurrentBuffer[ mCurrentBufferPointer++ ]; - - samplesAvailable--; - remaining--; - newFloatsToConsumeThisFrame--; + remaining = 0; + } + else + { + int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; + + while( remaining > 0 && samplesAvailable > 0 ) + { + currentFrame[ currentFramePointer++ ] = + (float)mCurrentBuffer[ mCurrentBufferPointer++ ]; + + samplesAvailable--; + remaining--; + newFloatsToConsumeThisFrame--; + } } } @@ -355,18 +364,25 @@ public class DFTProcessor implements Listener, { getNextBuffer(); } - - int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; - - if( samplesAvailable >= samplesToPurge ) + + if( mCurrentBuffer != null ) { - mCurrentBufferPointer += samplesToPurge; - samplesToPurge = 0; + int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; + + if( samplesAvailable >= samplesToPurge ) + { + mCurrentBufferPointer += samplesToPurge; + samplesToPurge = 0; + } + else + { + samplesToPurge -= samplesAvailable; + mCurrentBufferPointer = mCurrentBuffer.length; + } } else { - samplesToPurge -= samplesAvailable; - mCurrentBufferPointer = mCurrentBuffer.length; + samplesToPurge = 0; } } } diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index 1ffd2835..0d91979b 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -466,16 +466,20 @@ public class OverlayPanel extends JPanel double yAxis = 0; //Draw the system label and adjust the y-axis position + String system = channel.hasSystem() ? channel.getSystem().getName() : " "; + yAxis += drawLabel( graphics, - channel.getSystem().getName(), + system, this.getFont(), xAxis, yAxis, width ); //Draw the site label and adjust the y-axis position + String site = channel.hasSite() ? channel.getSite().getName() : " "; + yAxis += drawLabel( graphics, - channel.getSite().getName(), + site, this.getFont(), xAxis, yAxis, @@ -615,6 +619,7 @@ public class OverlayPanel extends JPanel switch( event.getEvent() ) { case CHANNEL_ADDED: + case CHANNEL_ENABLED: if( !mChannels.contains( channel ) ) { mChannels.add( channel ); @@ -630,7 +635,7 @@ public class OverlayPanel extends JPanel mChannels.remove( channel ); mVisibleChannels.remove( channel ); break; - case PROCESSING_STOPPED: + case CHANNEL_PROCESSING_STOPPED: if( channel.getChannelType() == ChannelType.TRAFFIC ) { mChannels.remove( channel ); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index f31d621e..bc3da83b 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -41,12 +41,14 @@ import javax.swing.JSeparator; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; +import module.decode.DecoderType; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; +import sample.SampleType; import sample.complex.ComplexBuffer; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -54,13 +56,12 @@ import settings.ColorSettingMenuItem; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; -import source.Source.SampleType; import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.Tuner; import source.tuner.TunerSelectionListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeListener; import spectrum.OverlayPanel.ChannelDisplay; import spectrum.converter.ComplexDecibelConverter; import spectrum.converter.DFTResultsConverter; @@ -76,7 +77,6 @@ import controller.ResourceManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; -import decode.DecoderType; import dsp.filter.Window.WindowType; /** @@ -515,15 +515,15 @@ public class SpectralDisplayPanel extends JPanel ArrayList channels = mOverlayPanel.getChannelsAtFrequency( frequency ); - for( Channel channel: channels ) - { - JMenu menu = channel.getContextMenu(); - - if( menu != null ) - { - contextMenu.add( menu ); - } - } +// for( Channel channel: channels ) +// { +// JMenu menu = channel.getContextMenu(); +// +// if( menu != null ) +// { +// contextMenu.add( menu ); +// } +// } if( !channels.isEmpty() ) { @@ -537,7 +537,7 @@ public class SpectralDisplayPanel extends JPanel JMenu decoderMenu = new JMenu( "Add Decoder" ); - for( DecoderType type: DecoderType.getAvailableDecoders() ) + for( DecoderType type: DecoderType.getPrimaryDecoders() ) { decoderMenu.add( new DecoderItem( mResourceManager, frequency, diff --git a/src/util/ArcTangent.java b/src/util/ArcTangent.java index 528717c0..234621b5 100644 --- a/src/util/ArcTangent.java +++ b/src/util/ArcTangent.java @@ -40,7 +40,7 @@ */ package util; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ArcTangent { @@ -124,7 +124,7 @@ public class ArcTangent * @param sample representing polar coordinates * @return - angle in radians */ - public static double getAngle( ComplexSample sample ) + public static double getAngle( Complex sample ) { return getAngle( ( (double) sample.inphase() ), ( (double) sample.quadrature() ) ); diff --git a/src/util/Oscillator.java b/src/util/Oscillator.java index e320f685..f003c4be 100644 --- a/src/util/Oscillator.java +++ b/src/util/Oscillator.java @@ -17,15 +17,15 @@ ******************************************************************************/ package util; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class Oscillator { private double mFrequency; private double mSampleRate; - private ComplexSample mAnglePerSample; - private ComplexSample mCurrentAngle = new ComplexSample( 0.0f, -1.0f ); + private Complex mAnglePerSample; + private Complex mCurrentAngle = new Complex( 0.0f, -1.0f ); /** * Oscillator produces complex or float samples corresponding to a sine wave @@ -42,17 +42,15 @@ public class Oscillator update(); } - public ComplexSample getCurrentAngle() - { - return mCurrentAngle; - } - + /** + * Updates the internal values after a frequency or sample rate change + */ private void update() { float anglePerSample = (float)( 2.0d * Math.PI * mFrequency / mSampleRate ); - mAnglePerSample = ComplexSample.fromAngle( anglePerSample ); + mAnglePerSample = Complex.fromAngle( anglePerSample ); } /** @@ -76,37 +74,35 @@ public class Oscillator /** * Steps the current angle by the angle per sample amount */ - private void rotate() + public void rotate() { mCurrentAngle.multiply( mAnglePerSample ); mCurrentAngle.fastNormalize(); } + public float inphase() + { + return mCurrentAngle.inphase(); + } + + public float quadrature() + { + return mCurrentAngle.quadrature(); + } + /** * Get next complex sample */ - public ComplexSample nextComplex() + public Complex getComplex() { - rotate(); return mCurrentAngle.copy(); } /** * Get the next float sample */ - public float nextFloat() + public float getFloat() { - rotate(); return mCurrentAngle.real(); } - - public static void main( String[] args ) - { - Oscillator o = new Oscillator( 1200, 2400 ); - - for( int x = 0; x < 20; x++ ) - { - System.out.println( o.nextComplex().toString() ); - } - } }