From 96d3996becb3ec456136b911b25b9fd96cbd12bf Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Mon, 18 Apr 2022 07:30:58 -0400 Subject: [PATCH] #1190 Tuner management redesign. Updates all tuners with direct threaded support. Incorporates USB Hotplug support. Adds black/white listing of tuners. Updates all tuner editors to have a common look and feel. Changes location of tuner configuration file and changes format to JSON. (#1208) Co-authored-by: Dennis Sheirer --- build.gradle | 2 +- .../github/dsheirer/alias/AliasFactory.java | 29 +- .../io/github/dsheirer/alias/AliasList.java | 29 +- .../io/github/dsheirer/alias/AliasModel.java | 29 +- .../broadcast/BroadcastConfiguration.java | 29 +- .../audio/broadcast/ConfiguredBroadcast.java | 29 +- .../icecast/codec/IcecastEncoder.java | 10 +- .../shoutcast/v2/ultravox/StreamMimeType.java | 10 +- .../v2/ultravox/UltravoxMessage.java | 10 +- .../v2/ultravox/UltravoxMessageFactory.java | 10 +- .../audio/codec/mbe/MBECallSequence.java | 2 +- .../convert/thumbdv/message/PacketField.java | 29 +- .../message/response/AmbeResponse.java | 29 +- .../dsheirer/audio/playback/AudioPanel.java | 12 +- .../buffer/ComplexCircularBuffer.java | 29 +- .../dsheirer/buffer/FloatCircularBuffer.java | 2 +- .../dsheirer/buffer/RealCircularBuffer.java | 10 +- .../channel/metadata/ChannelMetadata.java | 29 +- .../dsheirer/channel/state/DecoderState.java | 29 +- .../channel/state/MultiChannelState.java | 29 +- .../channel/state/SingleChannelState.java | 29 +- .../dsheirer/channel/state/StateMachine.java | 29 +- .../channel/state/TimeslotDecoderState.java | 29 +- .../dsheirer/controller/ControllerPanel.java | 12 +- .../channel/ChannelConversionRequest.java | 2 +- .../channel/ChannelProcessingManager.java | 31 +- .../channelizer/PolyphaseChannelManager.java | 52 +- .../decimate/DecimationFilterFactory.java | 2 +- .../HalfBandDecimationFilterAnalysis.java | 2 +- .../fir/remez/RemezFIRFilterDesigner.java | 19 + .../fir/remez/RemezFIRFilterDesigner2.java | 10 +- .../RemezFIRFilterDesignerWithLagrange.java | 10 +- .../dsheirer/dsp/window/WindowFactory.java | 2 +- .../java/io/github/dsheirer/edac/CRCUtil.java | 29 +- .../dsheirer/gui/JavaFxWindowManager.java | 19 +- .../java/io/github/dsheirer/gui/SDRTrunk.java | 77 +- .../gui/channelizer/ChannelizerViewer.java | 19 +- .../gui/channelizer/ChannelizerViewer2.java | 19 +- .../HeterodyneChannelizerViewer.java | 19 +- .../gui/channelizer/SynthesizerViewer.java | 12 +- .../gui/control/JFrequencyControl.java | 142 +- .../dsheirer/gui/editor/EmptyEditor.java | 31 - .../decoder/DecoderPaneFactory.java | 7 +- .../dsheirer/gui/playlist/PlaylistEditor.java | 8 +- .../playlist/PlaylistEditorApplication.java | 24 +- .../action/UnrecognizedActionEditor.java | 2 +- .../UnrecognizedIdentifierEditor.java | 2 +- .../channel/AMConfigurationEditor.java | 41 +- .../channel/ChannelConfigurationEditor.java | 18 +- .../ChannelConfigurationEditorFactory.java | 50 +- .../gui/playlist/channel/ChannelEditor.java | 38 +- .../channel/DMRConfigurationEditor.java | 41 +- .../channel/LTRConfigurationEditor.java | 42 +- .../channel/LTRNetConfigurationEditor.java | 41 +- .../channel/MPT1327ConfigurationEditor.java | 42 +- .../channel/NBFMConfigurationEditor.java | 42 +- .../channel/P25P1ConfigurationEditor.java | 42 +- .../channel/P25P2ConfigurationEditor.java | 37 +- .../channel/PassportConfigurationEditor.java | 37 +- .../channel/UnknownConfigurationEditor.java | 35 +- .../radioreference/ModeDecoderType.java | 2 +- .../radioreference/RadioReferenceDecoder.java | 2 +- .../gui/playlist/source/FrequencyEditor.java | 49 +- .../streaming/AbstractBroadcastEditor.java | 2 +- .../streaming/StreamEditorFactory.java | 29 +- .../ViewUserPreferenceEditorRequest.java | 27 +- .../directory/DirectoryPreferenceEditor.java | 90 +- .../io/github/dsheirer/identifier/Form.java | 29 +- .../dsheirer/identifier/Identifier.java | 27 +- .../dsheirer/identifier/IdentifierClass.java | 27 +- .../dsheirer/identifier/tone/AmbeTone.java | 2 +- .../dsheirer/module/decode/Decoder.java | 29 +- .../module/decode/DecoderFactory.java | 2 +- .../dsheirer/module/decode/DecoderType.java | 29 +- .../decode/dmr/DMRTrafficChannelManager.java | 29 +- .../decode/dmr/message/UnknownDMRMessage.java | 2 +- .../decode/dmr/message/data/DataMessage.java | 7 +- .../decode/dmr/message/data/SlotType.java | 7 +- .../data/csbk/hytera/HyteraAnnouncement.java | 2 +- .../message/data/csbk/standard/Protect.java | 2 +- .../standard/acknowledge/Acknowledge.java | 2 +- .../csbk/standard/ahoy/ServiceRadioCheck.java | 2 +- .../standard/announcement/Announcement.java | 2 +- .../data/header/PacketSequenceHeader.java | 2 +- .../hytera/HyteraProprietaryDataHeader.java | 2 +- .../motorola/MNISProprietaryDataHeader.java | 2 +- .../decode/dmr/message/data/usb/USBData.java | 2 +- .../dmr/message/type/AcknowledgeType.java | 19 + .../dmr/message/type/AnnouncementType.java | 2 +- .../dmr/message/type/ApplicationType.java | 2 +- .../decode/dmr/message/type/Gateway.java | 19 + .../module/decode/dmr/message/type/Model.java | 2 +- .../module/decode/dmr/message/type/PAR.java | 2 +- .../dmr/message/type/ServiceFunction.java | 2 +- .../decode/dmr/message/type/ServiceKind.java | 19 + .../decode/dmr/message/type/ServiceType.java | 19 + .../decode/dmr/message/type/Version.java | 2 +- .../module/decode/event/DecodeEvent.java | 29 +- .../module/decode/event/IDecodeEvent.java | 29 +- .../module/decode/ip/ars/ARSHeader.java | 7 +- .../decode/ip/cellocator/MCGPHeader.java | 7 +- .../ip/cellocator/MCGPMessageFactory.java | 7 +- .../decode/ip/cellocator/MCGPMessageType.java | 2 +- .../ip/cellocator/ModularRequestMessage.java | 2 +- .../ip/cellocator/ModularResponseMessage.java | 2 +- .../module/decode/ip/icmp/ICMPTypeCode.java | 2 +- .../module/decode/ip/lrrp/LRRPHeader.java | 2 +- .../module/decode/ip/lrrp/LRRPPacketType.java | 2 +- .../decode/ip/lrrp/token/RequestedTokens.java | 3 +- .../decode/ip/lrrp/token/TokenType.java | 2 +- .../module/decode/ip/xcmp/XCMPHeader.java | 2 +- .../decode/ip/xcmp/XCMPMessageType.java | 2 +- .../ltrnet/message/isw/LtrNetIswMessage.java | 7 +- .../ltrstandard/message/LTRMessage.java | 7 +- .../ltrstandard/message/UnknownMessage.java | 7 +- .../decode/mpt1327/MPT1327DecoderState.java | 29 +- .../module/decode/mpt1327/MPT1327Message.java | 29 +- .../decode/p25/audio/P25P2AudioModule.java | 2 +- .../decode/p25/phase1/P25P1DataUnitID.java | 7 +- .../p25/phase1/P25P1MessageProcessor.java | 29 +- .../P25P1NetworkConfigurationMonitor.java | 37 +- .../message/IFrequencyBandReceiver.java | 7 +- .../decode/p25/phase1/message/P25Message.java | 7 +- .../p25/phase1/message/P25MessageFactory.java | 7 +- .../standard/LCExtendedFunctionCommand.java | 29 +- .../sndcp/ActivateTdsContextRequest.java | 7 +- .../pdu/packet/sndcp/SNDCPMessage.java | 7 +- .../pdu/packet/sndcp/SNDCPMessageFactory.java | 7 +- .../message/pdu/response/ResponseMessage.java | 7 +- .../motorola/osp/MotorolaDenyResponse.java | 29 +- .../standard/isp/CancelServiceRequest.java | 29 +- .../standard/isp/UnitAcknowledgeResponse.java | 29 +- .../standard/osp/AcknowledgeResponse.java | 29 +- .../tsbk/standard/osp/DenyResponse.java | 29 +- .../tsbk/standard/osp/QueuedResponse.java | 29 +- .../p25/phase2/P25P2MessageProcessor.java | 29 +- .../p25/phase2/message/mac/MacMessage.java | 29 +- .../p25/phase2/message/mac/MacPduType.java | 29 +- .../mac/structure/AcknowledgeResponse.java | 29 +- .../message/mac/structure/DenyResponse.java | 29 +- .../message/mac/structure/QueuedResponse.java | 29 +- .../dsheirer/module/log/EventLogType.java | 35 +- .../dsheirer/playlist/PlaylistManager.java | 52 +- .../dsheirer/playlist/PlaylistUpdater.java | 29 +- .../dsheirer/preference/Preference.java | 7 +- .../directory/DirectoryPreference.java | 54 +- .../preference/source/ChannelizerType.java | 7 +- .../preference/source/TunerPreference.java | 7 +- .../dsheirer/record/RecorderFactory.java | 7 +- .../record/config/RecordConfiguration.java | 7 +- .../dsheirer/record/wave/AudioMetadata.java | 24 +- .../record/wave/AudioMetadataUtils.java | 13 +- .../record/wave/IRecordingStatusListener.java | 34 + .../record/wave/NativeBufferWaveRecorder.java | 140 +- .../io/github/dsheirer/settings/Settings.java | 41 +- .../dsheirer/settings/SettingsManager.java | 77 +- .../io/github/dsheirer/source/Source.java | 7 +- .../github/dsheirer/source/SourceEvent.java | 39 +- .../github/dsheirer/source/SourceManager.java | 120 -- .../dsheirer/source/mixer/ComplexMixer.java | 4 +- .../dsheirer/source/mixer/MixerReader.java | 216 +-- .../dsheirer/source/recording/Recording.java | 123 -- .../recording/RecordingConfiguration.java | 40 +- .../recording/RecordingSourceManager.java | 143 -- .../tuner/LoggingTunerErrorListener.java | 37 + .../dsheirer/source/tuner/MixerTunerType.java | 45 +- .../github/dsheirer/source/tuner/Tuner.java | 174 +- .../dsheirer/source/tuner/TunerClass.java | 362 +--- .../source/tuner/TunerController.java | 135 +- .../dsheirer/source/tuner/TunerEditor.java | 404 ---- .../dsheirer/source/tuner/TunerEvent.java | 69 +- .../dsheirer/source/tuner/TunerFactory.java | 107 ++ .../tuner/TunerFrequencyErrorMonitor.java | 29 +- .../dsheirer/source/tuner/TunerManager.java | 562 ------ .../dsheirer/source/tuner/TunerModel.java | 475 ----- .../tuner/TunerSpectralDisplayManager.java | 61 - .../dsheirer/source/tuner/TunerType.java | 129 +- .../dsheirer/source/tuner/TunerViewPanel.java | 288 --- .../source/tuner/airspy/AirspyTuner.java | 62 +- .../airspy/AirspyTunerConfiguration.java | 221 ++- .../tuner/airspy/AirspyTunerController.java | 494 ++--- .../tuner/airspy/AirspyTunerEditor.java | 1191 ++++++------ .../MultiFrequencyTunerChannelSource.java | 10 +- .../tuner/channel/TunerChannelSource.java | 4 + .../tuner/configuration/DisabledTuner.java | 41 + .../configuration/TunerConfiguration.java | 130 +- .../TunerConfigurationEditor.java | 43 - .../TunerConfigurationFactory.java | 109 +- .../TunerConfigurationManager.java | 315 ++++ .../TunerConfigurationModel.java | 378 ---- .../TunerConfigurationState.java | 71 + .../dsheirer/source/tuner/fcd/FCDTuner.java | 49 +- .../source/tuner/fcd/FCDTunerController.java | 343 ++-- .../fcd/proV1/FCD1TunerConfiguration.java | 49 +- .../tuner/fcd/proV1/FCD1TunerController.java | 312 +--- .../tuner/fcd/proV1/FCD1TunerEditor.java | 696 +++---- .../fcd/proplusV2/FCD2TunerConfiguration.java | 54 +- .../fcd/proplusV2/FCD2TunerController.java | 124 +- .../tuner/fcd/proplusV2/FCD2TunerEditor.java | 425 ++--- .../tuner/frequency/FrequencyController.java | 54 +- .../source/tuner/hackrf/HackRFTuner.java | 74 +- .../hackrf/HackRFTunerConfiguration.java | 50 +- .../tuner/hackrf/HackRFTunerController.java | 423 ++--- .../tuner/hackrf/HackRFTunerEditor.java | 616 +++---- .../manager/CenterFrequencyCalculator.java | 29 +- .../tuner/manager/ChannelSourceManager.java | 34 +- .../source/tuner/manager/DiscoveredTuner.java | 276 +++ .../tuner/manager/DiscoveredUSBTuner.java | 135 ++ .../FrequencyErrorCorrectionManager.java | 38 +- .../HeterodyneChannelSourceManager.java | 31 +- .../IDiscoveredTunerStatusListener.java | 34 + .../manager/PassThroughSourceManager.java | 56 +- .../PolyphaseChannelSourceManager.java | 16 +- .../source/tuner/manager/TunerManager.java | 560 ++++++ .../TunerStatus.java} | 55 +- .../recording/AddRecordingTunerDialog.java | 62 +- .../tuner/recording/RecordingTuner.java | 44 +- .../RecordingTunerConfiguration.java | 42 +- .../RecordingTunerConfigurationEditor.java | 173 -- .../recording/RecordingTunerController.java | 44 +- .../tuner/recording/RecordingTunerEditor.java | 126 ++ .../source/tuner/rtl/EmbeddedTuner.java | 117 ++ .../source/tuner/rtl/RTL2832Tuner.java | 68 +- .../tuner/rtl/RTL2832TunerConfiguration.java | 64 + .../tuner/rtl/RTL2832TunerController.java | 1271 ++++++------- .../tuner/rtl/RTL2832UnknownTunerEditor.java | 102 ++ ...rController.java => E4KEmbeddedTuner.java} | 1627 ++++++++--------- .../tuner/rtl/e4k/E4KTunerConfiguration.java | 100 +- .../source/tuner/rtl/e4k/E4KTunerEditor.java | 736 ++++---- ...ontroller.java => R820TEmbeddedTuner.java} | 497 ++--- .../rtl/r820t/R820TTunerConfiguration.java | 123 +- .../tuner/rtl/r820t/R820TTunerEditor.java | 822 ++++----- .../dsheirer/source/tuner/test/TestTuner.java | 45 +- .../tuner/test/TestTunerController.java | 25 +- .../tuner/ui/DiscoveredTunerEditor.java | 128 ++ .../source/tuner/ui/DiscoveredTunerModel.java | 500 +++++ .../source/tuner/ui/EmptyTunerEditor.java | 42 + .../dsheirer/source/tuner/ui/TunerEditor.java | 707 +++++++ .../tuner/ui/TunerSpectralDisplayManager.java | 102 ++ .../source/tuner/ui/TunerViewPanel.java | 210 +++ .../source/tuner/usb/USBMasterProcessor.java | 168 -- .../tuner/usb/USBTransferProcessor.java | 587 ------ .../source/tuner/usb/USBTunerController.java | 661 ++++++- .../source/tuner/usb/USBTunerDevice.java | 44 - .../dsheirer/source/tuner/usb/USBUtils.java | 103 -- .../dsheirer/source/tuner/usb/UsbUtils.java | 141 ++ .../usb/converter/ByteSampleConverter.java | 80 - .../usb/converter/NativeBufferConverter.java | 61 - .../converter/SignedByteSampleConverter.java | 89 - .../dsheirer/spectrum/ShowTunerMenuItem.java | 41 +- .../spectrum/SpectralDisplayAdjuster.java | 19 + .../spectrum/SpectralDisplayPanel.java | 26 +- .../dsheirer/spectrum/SpectrumFrame.java | 36 +- .../dsheirer/spectrum/SpectrumPanel.java | 10 +- .../io/github/dsheirer/util/SwingUtils.java | 43 + .../swingx/mapviewer/WaypointPainter.java | 22 +- .../swingx/mapviewer/WaypointRenderer.java | 21 +- .../org/jdesktop/swingx/painter/Painter.java | 30 +- .../swingx/util/GraphicsUtilities.java | 55 +- 259 files changed, 11974 insertions(+), 12454 deletions(-) delete mode 100644 src/main/java/io/github/dsheirer/gui/editor/EmptyEditor.java create mode 100644 src/main/java/io/github/dsheirer/record/wave/IRecordingStatusListener.java delete mode 100644 src/main/java/io/github/dsheirer/source/SourceManager.java delete mode 100644 src/main/java/io/github/dsheirer/source/recording/Recording.java delete mode 100644 src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/LoggingTunerErrorListener.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerManager.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerModel.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerSpectralDisplayManager.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/configuration/DisabledTuner.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationState.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredUSBTuner.java rename src/main/java/io/github/dsheirer/source/tuner/{ => manager}/FrequencyErrorCorrectionManager.java (84%) create mode 100644 src/main/java/io/github/dsheirer/source/tuner/manager/IDiscoveredTunerStatusListener.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java rename src/main/java/io/github/dsheirer/source/tuner/{usb/converter/NativeSampleBuffer.java => manager/TunerStatus.java} (51%) delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfigurationEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/rtl/EmbeddedTuner.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerConfiguration.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832UnknownTunerEditor.java rename src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/{E4KTunerController.java => E4KEmbeddedTuner.java} (58%) rename src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/{R820TTunerController.java => R820TEmbeddedTuner.java} (73%) create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ui/EmptyTunerEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ui/TunerSpectralDisplayManager.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerDevice.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/USBUtils.java create mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/UsbUtils.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/converter/ByteSampleConverter.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeBufferConverter.java delete mode 100644 src/main/java/io/github/dsheirer/source/tuner/usb/converter/SignedByteSampleConverter.java create mode 100644 src/main/java/io/github/dsheirer/util/SwingUtils.java diff --git a/build.gradle b/build.gradle index e8c84e65..c9dceb35 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ dependencies { //mavenCentral/jcenter imports implementation 'ch.qos.logback:logback-classic:1.2.3' implementation 'ch.qos.logback:logback-core:1.2.3' - implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.2' implementation 'com.fazecast:jSerialComm:2.5.0' implementation 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1' implementation 'com.github.jiconfont:jiconfont-javafx:1.0.0' diff --git a/src/main/java/io/github/dsheirer/alias/AliasFactory.java b/src/main/java/io/github/dsheirer/alias/AliasFactory.java index 095d85ee..12c40e47 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasFactory.java +++ b/src/main/java/io/github/dsheirer/alias/AliasFactory.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.alias; diff --git a/src/main/java/io/github/dsheirer/alias/AliasList.java b/src/main/java/io/github/dsheirer/alias/AliasList.java index ce96e121..144cd9bb 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasList.java +++ b/src/main/java/io/github/dsheirer/alias/AliasList.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.alias; diff --git a/src/main/java/io/github/dsheirer/alias/AliasModel.java b/src/main/java/io/github/dsheirer/alias/AliasModel.java index 7300f756..9a5ca904 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasModel.java +++ b/src/main/java/io/github/dsheirer/alias/AliasModel.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.alias; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java index 674d897a..b1e6753c 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastConfiguration.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.audio.broadcast; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/ConfiguredBroadcast.java b/src/main/java/io/github/dsheirer/audio/broadcast/ConfiguredBroadcast.java index c1f4869b..45f4812d 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/ConfiguredBroadcast.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/ConfiguredBroadcast.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.audio.broadcast; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/codec/IcecastEncoder.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/codec/IcecastEncoder.java index b63d8e35..e37cd47a 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/codec/IcecastEncoder.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/codec/IcecastEncoder.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.audio.broadcast.icecast.codec; import org.apache.mina.core.buffer.IoBuffer; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/StreamMimeType.java b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/StreamMimeType.java index d963b1ed..8d6fc6b9 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/StreamMimeType.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/StreamMimeType.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox; import io.github.dsheirer.audio.broadcast.BroadcastFormat; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessage.java b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessage.java index 59561de4..4c8aeb72 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessage.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessage.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox; import io.github.dsheirer.bits.BinaryMessage; diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessageFactory.java b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessageFactory.java index 6f6603e7..40262110 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessageFactory.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/shoutcast/v2/ultravox/UltravoxMessageFactory.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.audio.broadcast.shoutcast.v2.ultravox; public class UltravoxMessageFactory diff --git a/src/main/java/io/github/dsheirer/audio/codec/mbe/MBECallSequence.java b/src/main/java/io/github/dsheirer/audio/codec/mbe/MBECallSequence.java index 05b71c6a..c693749b 100644 --- a/src/main/java/io/github/dsheirer/audio/codec/mbe/MBECallSequence.java +++ b/src/main/java/io/github/dsheirer/audio/codec/mbe/MBECallSequence.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/PacketField.java b/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/PacketField.java index 1b15f993..5f9b264e 100644 --- a/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/PacketField.java +++ b/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/PacketField.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.audio.convert.thumbdv.message; diff --git a/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/response/AmbeResponse.java b/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/response/AmbeResponse.java index b5183698..364309ee 100644 --- a/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/response/AmbeResponse.java +++ b/src/main/java/io/github/dsheirer/audio/convert/thumbdv/message/response/AmbeResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.audio.convert.thumbdv.message.response; diff --git a/src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java b/src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java index e2442f0f..4143a7fe 100644 --- a/src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioPanel.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.audio.playback; @@ -30,7 +29,6 @@ import io.github.dsheirer.icon.IconModel; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.SourceManager; import io.github.dsheirer.source.mixer.MixerChannelConfiguration; import jiconfont.icons.font_awesome.FontAwesome; import jiconfont.swing.IconFontSwing; @@ -69,7 +67,6 @@ public class AudioPanel extends JPanel implements Listener private IconModel mIconModel; private SettingsManager mSettingsManager; - private SourceManager mSourceManager; private IAudioController mController; private AliasModel mAliasModel; private UserPreferences mUserPreferences; @@ -78,11 +75,10 @@ public class AudioPanel extends JPanel implements Listener private AudioChannelsPanel mAudioChannelsPanel; public AudioPanel(IconModel iconModel, UserPreferences userPreferences, SettingsManager settingsManager, - SourceManager sourceManager, IAudioController controller, AliasModel aliasModel) + IAudioController controller, AliasModel aliasModel) { mIconModel = iconModel; mSettingsManager = settingsManager; - mSourceManager = sourceManager; mController = controller; mAliasModel = aliasModel; mUserPreferences = userPreferences; diff --git a/src/main/java/io/github/dsheirer/buffer/ComplexCircularBuffer.java b/src/main/java/io/github/dsheirer/buffer/ComplexCircularBuffer.java index db56da97..e39e94a4 100644 --- a/src/main/java/io/github/dsheirer/buffer/ComplexCircularBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/ComplexCircularBuffer.java @@ -1,20 +1,21 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.buffer; import io.github.dsheirer.sample.complex.Complex; diff --git a/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java b/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java index 03efc955..9313eab3 100644 --- a/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/buffer/RealCircularBuffer.java b/src/main/java/io/github/dsheirer/buffer/RealCircularBuffer.java index f4274c8a..14a7a97a 100644 --- a/src/main/java/io/github/dsheirer/buffer/RealCircularBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/RealCircularBuffer.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.buffer; /** diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java index ad8b5a21..ad0dbcfc 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.channel.metadata; diff --git a/src/main/java/io/github/dsheirer/channel/state/DecoderState.java b/src/main/java/io/github/dsheirer/channel/state/DecoderState.java index a50c8f6e..811a8365 100644 --- a/src/main/java/io/github/dsheirer/channel/state/DecoderState.java +++ b/src/main/java/io/github/dsheirer/channel/state/DecoderState.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.channel.state; diff --git a/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java b/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java index b9dd869c..8e72c8ab 100644 --- a/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java +++ b/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.channel.state; diff --git a/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java b/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java index c532eef2..c80e018b 100644 --- a/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java +++ b/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.channel.state; diff --git a/src/main/java/io/github/dsheirer/channel/state/StateMachine.java b/src/main/java/io/github/dsheirer/channel/state/StateMachine.java index 70f8db57..eacdc6c6 100644 --- a/src/main/java/io/github/dsheirer/channel/state/StateMachine.java +++ b/src/main/java/io/github/dsheirer/channel/state/StateMachine.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.channel.state; diff --git a/src/main/java/io/github/dsheirer/channel/state/TimeslotDecoderState.java b/src/main/java/io/github/dsheirer/channel/state/TimeslotDecoderState.java index 622606bf..85d5bd0c 100644 --- a/src/main/java/io/github/dsheirer/channel/state/TimeslotDecoderState.java +++ b/src/main/java/io/github/dsheirer/channel/state/TimeslotDecoderState.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.channel.state; diff --git a/src/main/java/io/github/dsheirer/controller/ControllerPanel.java b/src/main/java/io/github/dsheirer/controller/ControllerPanel.java index f3f86e68..54ce7164 100644 --- a/src/main/java/io/github/dsheirer/controller/ControllerPanel.java +++ b/src/main/java/io/github/dsheirer/controller/ControllerPanel.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +30,8 @@ import io.github.dsheirer.map.MapService; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.SourceManager; -import io.github.dsheirer.source.tuner.TunerViewPanel; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerViewPanel; import jiconfont.icons.font_awesome.FontAwesome; import jiconfont.swing.IconFontSwing; import net.miginfocom.swing.MigLayout; @@ -59,13 +59,13 @@ public class ControllerPanel extends JPanel public ControllerPanel(PlaylistManager playlistManager, AudioPlaybackManager audioPlaybackManager, IconModel iconModel, MapService mapService, SettingsManager settingsManager, - SourceManager sourceManager, UserPreferences userPreferences) + TunerManager tunerManager, UserPreferences userPreferences) { - mAudioPanel = new AudioPanel(iconModel, userPreferences, settingsManager, sourceManager, audioPlaybackManager, + mAudioPanel = new AudioPanel(iconModel, userPreferences, settingsManager, audioPlaybackManager, playlistManager.getAliasModel()); mNowPlayingPanel = new NowPlayingPanel(playlistManager, iconModel, userPreferences); mMapPanel = new MapPanel(mapService, playlistManager.getAliasModel(), iconModel, settingsManager); - mTunerManagerPanel = new TunerViewPanel(sourceManager.getTunerModel(), userPreferences); + mTunerManagerPanel = new TunerViewPanel(tunerManager, userPreferences); init(); } diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelConversionRequest.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelConversionRequest.java index 60b3078b..1a51f4ec 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelConversionRequest.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelConversionRequest.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java index 642a540d..36a3353f 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java @@ -44,15 +44,15 @@ import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.Source; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.SourceManager; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.util.ThreadPool; import javafx.application.Platform; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.awt.*; +import java.awt.GraphicsEnvironment; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -84,7 +84,7 @@ public class ChannelProcessingManager implements Listener private ChannelMapModel mChannelMapModel; private ChannelMetadataModel mChannelMetadataModel; private EventLogManager mEventLogManager; - private SourceManager mSourceManager; + private TunerManager mTunerManager; private AliasModel mAliasModel; private UserPreferences mUserPreferences; private List mLoggedFrequencies = new ArrayList<>(); @@ -95,16 +95,16 @@ public class ChannelProcessingManager implements Listener * * @param channelMapModel containing channel maps defined by the user * @param eventLogManager for adding event loggers to channels - * @param sourceManager for obtaining a tuner channel source for the channel + * @param tunerManager for obtaining a tuner channel source for the channel * @param aliasModel for aliasing of identifiers produced by the channel * @param userPreferences for user defined behavior and settings */ public ChannelProcessingManager(ChannelMapModel channelMapModel, EventLogManager eventLogManager, - SourceManager sourceManager, AliasModel aliasModel, UserPreferences userPreferences) + TunerManager tunerManager, AliasModel aliasModel, UserPreferences userPreferences) { mChannelMapModel = channelMapModel; mEventLogManager = eventLogManager; - mSourceManager = sourceManager; + mTunerManager = tunerManager; mAliasModel = aliasModel; mUserPreferences = userPreferences; mChannelMetadataModel = new ChannelMetadataModel(); @@ -301,7 +301,7 @@ public class ChannelProcessingManager implements Listener try { - source = mSourceManager.getSource(channel.getSourceConfiguration(), + source = mTunerManager.getSource(channel.getSourceConfiguration(), channel.getDecodeConfiguration().getChannelSpecification()); } catch(SourceException se) @@ -720,7 +720,9 @@ public class ChannelProcessingManager implements Listener { @Override public void receive(SourceEvent sourceEvent) { - if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_ERROR_STATE && sourceEvent.getSource() != null) + if((sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_ERROR_STATE || + sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_TUNER_SHUTDOWN) && + sourceEvent.getSource() != null) { Channel toShutdown = null; @@ -744,7 +746,18 @@ public class ChannelProcessingManager implements Listener if(toShutdown != null) { - mLog.warn("Channel source error detected - stopping channel [" + toShutdown.getName() + "]"); + if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_ERROR_STATE) + { + mLog.warn("Channel source error detected - stopping channel [" + toShutdown.getName() + "]"); + } + else if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_TUNER_SHUTDOWN) + { + mLog.warn("Tuner removal detected - stopping channel [" + toShutdown.getName() + "]"); + } + else + { + mLog.warn("Source event error - stopping channel [" + toShutdown.getName() + "]"); + } try { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java index 36e8a749..9963de6e 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -41,6 +41,7 @@ import org.apache.commons.math3.util.FastMath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -84,6 +85,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor private NativeBufferReceiver mNativeBufferReceiver = new NativeBufferReceiver(); private Dispatcher mBufferDispatcher; private Map mOutputProcessorFilters = new HashMap<>(); + private boolean mRunning = true; /** * Creates a polyphase channel manager instance. @@ -126,6 +128,25 @@ public class PolyphaseChannelManager implements ISourceEventProcessor this(tunerController, tunerController.getFrequency(), tunerController.getSampleRate()); } + public void stopAllChannels() + { + mRunning = false; + + List toStop = new ArrayList<>(mChannelSources); + + for(TunerChannelSource tunerChannelSource: toStop) + { + try + { + tunerChannelSource.process(SourceEvent.tunerShutdown(tunerChannelSource)); + } + catch(SourceException se) + { + mLog.error("Error stopping tuner channel source for tuner shutdown"); + } + } + } + /** * Signals to all provisioned tuner channel sources that the source complex buffer provider has an error and can * no longer provide channels, so that the tuner channel source can notify the consumer of the error state. @@ -164,24 +185,27 @@ public class PolyphaseChannelManager implements ISourceEventProcessor { PolyphaseChannelSource channelSource = null; - List polyphaseIndexes = mChannelCalculator.getChannelIndexes(tunerChannel); - - IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes); - - if(outputProcessor != null) + if(mRunning) { - long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes); + List polyphaseIndexes = mChannelCalculator.getChannelIndexes(tunerChannel); - try - { - channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener, - mChannelCalculator.getChannelSampleRate(), centerFrequency); + IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes); - mChannelSources.add(channelSource); - } - catch(FilterDesignException fde) + if(outputProcessor != null) { - mLog.debug("Couldn't design final output low pass filter for polyphase channel source"); + long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes); + + try + { + channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener, + mChannelCalculator.getChannelSampleRate(), centerFrequency); + + mChannelSources.add(channelSource); + } + catch(FilterDesignException fde) + { + mLog.debug("Couldn't design final output low pass filter for polyphase channel source"); + } } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java index e82337f5..56841dcd 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java @@ -76,7 +76,7 @@ public class DecimationFilterFactory */ public static int getDecimationRate(int requestedRate) { - if(requestedRate < 2) + if(requestedRate < 0) { throw new IllegalArgumentException("Requested decimation rate must be greater than 2"); } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java b/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java index 763c695f..f17a1d40 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java index e813764a..54d53119 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.dsp.filter.fir.remez; import io.github.dsheirer.dsp.filter.design.FilterDesignException; diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java index 08443b48..c69415ef 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.dsp.filter.fir.remez; import io.github.dsheirer.dsp.filter.design.FilterDesignException; diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java index 0f8cb796..89156f8e 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.dsp.filter.fir.remez; import io.github.dsheirer.dsp.filter.design.FilterDesignException; diff --git a/src/main/java/io/github/dsheirer/dsp/window/WindowFactory.java b/src/main/java/io/github/dsheirer/dsp/window/WindowFactory.java index 9a52ec38..cd769502 100644 --- a/src/main/java/io/github/dsheirer/dsp/window/WindowFactory.java +++ b/src/main/java/io/github/dsheirer/dsp/window/WindowFactory.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/edac/CRCUtil.java b/src/main/java/io/github/dsheirer/edac/CRCUtil.java index d7b5fd53..4b0b08a2 100644 --- a/src/main/java/io/github/dsheirer/edac/CRCUtil.java +++ b/src/main/java/io/github/dsheirer/edac/CRCUtil.java @@ -1,20 +1,21 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.edac; import io.github.dsheirer.bits.BinaryMessage; diff --git a/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java b/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java index f7529795..63de28a7 100644 --- a/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java +++ b/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java @@ -41,10 +41,7 @@ import io.github.dsheirer.jmbe.JmbeEditorRequest; import io.github.dsheirer.module.log.EventLogManager; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.SourceManager; -import io.github.dsheirer.source.tuner.TunerModel; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.application.Application; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; @@ -79,6 +76,7 @@ public class JavaFxWindowManager extends Application private JmbeEditor mJmbeEditor; private PlaylistEditor mPlaylistEditor; private PlaylistManager mPlaylistManager; + private TunerManager mTunerManager; private UserPreferences mUserPreferences; private UserPreferencesEditor mUserPreferencesEditor; @@ -91,9 +89,10 @@ public class JavaFxWindowManager extends Application /** * Constructs an instance. Note: this constructor is used for Swing applications. */ - public JavaFxWindowManager(UserPreferences userPreferences, PlaylistManager playlistManager) + public JavaFxWindowManager(UserPreferences userPreferences, TunerManager tunerManager, PlaylistManager playlistManager) { mUserPreferences = userPreferences; + mTunerManager = tunerManager; mPlaylistManager = playlistManager; setup(); @@ -106,12 +105,10 @@ public class JavaFxWindowManager extends Application { mUserPreferences = new UserPreferences(); AliasModel aliasModel = new AliasModel(); - TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel(); - TunerModel tunerModel = new TunerModel(tunerConfigurationModel); - SourceManager sourceManager = new SourceManager(tunerModel, new SettingsManager(tunerConfigurationModel), - mUserPreferences); EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences); - mPlaylistManager = new PlaylistManager(mUserPreferences, sourceManager, aliasModel, eventLogManager, new IconModel()); + mTunerManager = new TunerManager(mUserPreferences); + mTunerManager.start(); + mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, new IconModel()); mPlaylistManager.init(); setup(); } @@ -274,7 +271,7 @@ public class JavaFxWindowManager extends Application { if(mPlaylistEditor == null) { - mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mUserPreferences); + mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mTunerManager, mUserPreferences); } return mPlaylistEditor; diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index 3b212860..70c5a114 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -48,12 +48,11 @@ import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.record.AudioRecordingManager; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.SourceManager; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerEvent; -import io.github.dsheirer.source.tuner.TunerModel; -import io.github.dsheirer.source.tuner.TunerSpectralDisplayManager; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerSpectralDisplayManager; import io.github.dsheirer.spectrum.ClearTunerMenuItem; import io.github.dsheirer.spectrum.ShowTunerMenuItem; import io.github.dsheirer.spectrum.SpectralDisplayPanel; @@ -69,11 +68,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; -import javax.swing.*; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.KeyStroke; +import javax.swing.UIManager; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.plaf.metal.MetalLookAndFeel; -import java.awt.*; +import java.awt.AWTException; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +import java.awt.Robot; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -105,13 +118,13 @@ public class SDRTrunk implements Listener private ControllerPanel mControllerPanel; private IconModel mIconModel = new IconModel(); private PlaylistManager mPlaylistManager; - private SourceManager mSourceManager; private SettingsManager mSettingsManager; private SpectralDisplayPanel mSpectralPanel; private JFrame mMainGui; private JideSplitPane mSplitPane; private JavaFxWindowManager mJavaFxWindowManager; private UserPreferences mUserPreferences = new UserPreferences(); + private TunerManager mTunerManager; private ApplicationLog mApplicationLog; private String mTitle; @@ -160,19 +173,18 @@ public class SDRTrunk implements Listener //Register FontAwesome so we can use the fonts in Swing windows IconFontSwing.register(FontAwesome.getIconFont()); - TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel(); - TunerModel tunerModel = new TunerModel(tunerConfigurationModel); + mTunerManager = new TunerManager(mUserPreferences); + mTunerManager.start(); - mSettingsManager = new SettingsManager(tunerConfigurationModel); - mSourceManager = new SourceManager(tunerModel, mSettingsManager, mUserPreferences); + mSettingsManager = new SettingsManager(); AliasModel aliasModel = new AliasModel(); EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences); - mPlaylistManager = new PlaylistManager(mUserPreferences, mSourceManager, aliasModel, eventLogManager, mIconModel); + mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, mIconModel); if(!GraphicsEnvironment.isHeadless()) { - mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mPlaylistManager); + mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mTunerManager, mPlaylistManager); } CalibrationManager calibrationManager = CalibrationManager.getInstance(mUserPreferences); @@ -203,15 +215,15 @@ public class SDRTrunk implements Listener if(!GraphicsEnvironment.isHeadless()) { mControllerPanel = new ControllerPanel(mPlaylistManager, audioPlaybackManager, mIconModel, mapService, - mSettingsManager, mSourceManager, mUserPreferences); + mSettingsManager, mTunerManager, mUserPreferences); } - mSpectralPanel = new SpectralDisplayPanel(mPlaylistManager, mSettingsManager, tunerModel); + mSpectralPanel = new SpectralDisplayPanel(mPlaylistManager, mSettingsManager, mTunerManager.getDiscoveredTunerModel()); TunerSpectralDisplayManager tunerSpectralDisplayManager = new TunerSpectralDisplayManager(mSpectralPanel, - mPlaylistManager, mSettingsManager, tunerModel); - tunerModel.addListener(tunerSpectralDisplayManager); - tunerModel.addListener(this); + mPlaylistManager, mSettingsManager, mTunerManager.getDiscoveredTunerModel()); + mTunerManager.getDiscoveredTunerModel().addListener(tunerSpectralDisplayManager); + mTunerManager.getDiscoveredTunerModel().addListener(this); mPlaylistManager.init(); @@ -227,8 +239,6 @@ public class SDRTrunk implements Listener initGUI(); } - tunerModel.requestFirstTunerDisplay(); - //Start the gui EventQueue.invokeLater(() -> { try @@ -236,6 +246,8 @@ public class SDRTrunk implements Listener if(!GraphicsEnvironment.isHeadless()) { mMainGui.setVisible(true); + Tuner tuner = tunerSpectralDisplayManager.showFirstTuner(); + updateTitle(tuner); } if(calibrating && !GraphicsEnvironment.isHeadless()) @@ -495,7 +507,8 @@ public class SDRTrunk implements Listener mLog.info("Stopping spectral display ..."); mSpectralPanel.clearTuner(); - mSourceManager.shutdown(); + mLog.info("Stopping tuners ..."); + mTunerManager.stop(); mLog.info("Shutdown complete."); mApplicationLog.stop(); } @@ -612,7 +625,23 @@ public class SDRTrunk implements Listener { if(event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY) { - mMainGui.setTitle(mTitle + " - " + event.getTuner().getName()); + updateTitle(event.getTuner()); + } + } + + /** + * Updates the title bar with the tuner name + * @param tuner optional + */ + private void updateTitle(Tuner tuner) + { + if(tuner != null) + { + mMainGui.setTitle(mTitle + " - " + tuner.getPreferredName()); + } + else + { + mMainGui.setTitle(mTitle); } } @@ -662,9 +691,9 @@ public class SDRTrunk implements Listener { removeAll(); - for(Tuner tuner : mSourceManager.getTunerModel().getTuners()) + for(DiscoveredTuner discoveredTuner: mTunerManager.getAvailableTuners()) { - add(new ShowTunerMenuItem(mSourceManager.getTunerModel(), tuner)); + add(new ShowTunerMenuItem(mTunerManager.getDiscoveredTunerModel(), discoveredTuner.getTuner())); } } diff --git a/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer.java b/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer.java index b70e8f8e..8b356c3b 100644 --- a/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer.java +++ b/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer.java @@ -20,17 +20,16 @@ package io.github.dsheirer.gui.channelizer; import io.github.dsheirer.buffer.FloatNativeBuffer; import io.github.dsheirer.buffer.INativeBuffer; -import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.ComplexSamples; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.LoggingTunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.test.TestTuner; import io.github.dsheirer.spectrum.ComplexDftProcessor; import io.github.dsheirer.spectrum.DFTSize; @@ -40,10 +39,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JToggleButton; +import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.*; +import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; @@ -57,7 +62,7 @@ public class ChannelizerViewer extends JFrame private static final int CHANNEL_BANDWIDTH = 12500; private static final int CHANNEL_FFT_FRAME_RATE = 20; - private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel()); + private SettingsManager mSettingsManager = new SettingsManager(); private JPanel mPrimaryPanel; private JPanel mControlPanel; private JLabel mToneFrequencyLabel; @@ -78,7 +83,7 @@ public class ChannelizerViewer extends JFrame */ public ChannelizerViewer(int channelsPerRow) { - mTestTuner = new TestTuner(new UserPreferences()); + mTestTuner = new TestTuner(new LoggingTunerErrorListener()); mChannelCount = (int)(mTestTuner.getTunerController().getBandwidth() / CHANNEL_BANDWIDTH); mChannelsPerRow = channelsPerRow; @@ -518,7 +523,7 @@ public class ChannelizerViewer extends JFrame } else { - TestTuner tuner = new TestTuner(new UserPreferences()); + TestTuner tuner = new TestTuner(new LoggingTunerErrorListener()); List tunerChannels = getTunerChannels(tuner); diff --git a/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer2.java b/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer2.java index cde0495c..99f7c113 100644 --- a/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer2.java +++ b/src/main/java/io/github/dsheirer/gui/channelizer/ChannelizerViewer2.java @@ -20,18 +20,17 @@ package io.github.dsheirer.gui.channelizer; import io.github.dsheirer.buffer.FloatNativeBuffer; import io.github.dsheirer.buffer.INativeBuffer; -import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.ComplexSamples; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.LoggingTunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.channel.ChannelSpecification; import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.test.TestTuner; import io.github.dsheirer.spectrum.ComplexDftProcessor; import io.github.dsheirer.spectrum.DFTSize; @@ -41,10 +40,18 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JToggleButton; +import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.*; +import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; @@ -56,7 +63,7 @@ public class ChannelizerViewer2 extends JFrame private static final int CHANNEL_BANDWIDTH = 12500; - private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel()); + private SettingsManager mSettingsManager = new SettingsManager(); private JPanel mPrimaryPanel; private JPanel mControlPanel; private JButton mTopFrameAddChannelButton; @@ -68,7 +75,7 @@ public class ChannelizerViewer2 extends JFrame private ChannelArrayPanel mBottomChannelArrayPanel; private DFTSize mMainPanelDFTSize = DFTSize.FFT32768; private DFTSize mChannelPanelDFTSize = DFTSize.FFT04096; - private TestTuner mTestTuner = new TestTuner(new UserPreferences()); + private TestTuner mTestTuner = new TestTuner(new LoggingTunerErrorListener()); /** * GUI Test utility for researching polyphase channelizers. diff --git a/src/main/java/io/github/dsheirer/gui/channelizer/HeterodyneChannelizerViewer.java b/src/main/java/io/github/dsheirer/gui/channelizer/HeterodyneChannelizerViewer.java index 99dbd6e2..e6e8bd2e 100644 --- a/src/main/java/io/github/dsheirer/gui/channelizer/HeterodyneChannelizerViewer.java +++ b/src/main/java/io/github/dsheirer/gui/channelizer/HeterodyneChannelizerViewer.java @@ -20,18 +20,17 @@ package io.github.dsheirer.gui.channelizer; import io.github.dsheirer.buffer.FloatNativeBuffer; import io.github.dsheirer.buffer.INativeBuffer; -import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.ComplexSamples; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.LoggingTunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.channel.ChannelSpecification; import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.test.TestTuner; import io.github.dsheirer.spectrum.ComplexDftProcessor; import io.github.dsheirer.spectrum.DFTSize; @@ -41,10 +40,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JToggleButton; +import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.*; +import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; @@ -58,7 +63,7 @@ public class HeterodyneChannelizerViewer extends JFrame private static final int CHANNEL_BANDWIDTH = 12500; private static final int CHANNEL_FFT_FRAME_RATE = 20; - private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel()); + private SettingsManager mSettingsManager = new SettingsManager(); private JPanel mPrimaryPanel; private JPanel mControlPanel; private JLabel mToneFrequencyLabel; @@ -77,7 +82,7 @@ public class HeterodyneChannelizerViewer extends JFrame */ public HeterodyneChannelizerViewer() { - mTestTuner = new TestTuner(new UserPreferences()); + mTestTuner = new TestTuner(new LoggingTunerErrorListener()); mChannelCount = 5; mChannelsPerRow = 5; init(); @@ -516,7 +521,7 @@ public class HeterodyneChannelizerViewer extends JFrame } else { - TestTuner tuner = new TestTuner(new UserPreferences()); + TestTuner tuner = new TestTuner(new LoggingTunerErrorListener()); List tunerChannels = getTunerChannels(tuner); diff --git a/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java b/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java index 4518134e..1167a036 100644 --- a/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java +++ b/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java @@ -29,7 +29,6 @@ import io.github.dsheirer.dsp.oscillator.OscillatorFactory; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.ComplexSamples; import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.spectrum.ComplexDftProcessor; import io.github.dsheirer.spectrum.DFTSize; import io.github.dsheirer.spectrum.SpectrumPanel; @@ -39,10 +38,15 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.*; +import java.awt.Dimension; +import java.awt.EventQueue; import java.util.concurrent.TimeUnit; public class SynthesizerViewer extends JFrame @@ -54,7 +58,7 @@ public class SynthesizerViewer extends JFrame private static final int CHANNEL_FFT_FRAME_RATE = 20; //frames per second private static final int DATA_GENERATOR_FRAME_RATE = 50; //frames per second - private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel()); + private SettingsManager mSettingsManager = new SettingsManager(); private JPanel mPrimaryPanel; private PrimarySpectrumPanel mPrimarySpectrumPanel; private ChannelPanel mChannel1Panel; diff --git a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java index 402d4813..ac94e023 100644 --- a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java +++ b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,7 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.gui.control; import io.github.dsheirer.source.ISourceEventProcessor; @@ -69,12 +70,6 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor init(); } - public JFrequencyControl(long value) - { - this(); - mFrequency = value; - } - private void init() { setLayout(new MigLayout("insets 0 0 0 0", "[]0[]", "")); @@ -91,8 +86,7 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor } catch(ParseException e) { - mLog.error("JFrequencyControl - parse exception " - + "constructing a digit - " + e); + mLog.error("JFrequencyControl - parse exception constructing a digit - " + e); } if(digit != null) @@ -105,15 +99,11 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor if(x == 6) { - JLabel period = new JLabel("."); - - add(period); + add(new JLabel(". ")); } } } - add(new JLabel(" MHz")); - /** * Create a blank cursor to use when the mouse is over the digits */ @@ -123,11 +113,7 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor //Create image for cursor using empty array Image cursorImage = Toolkit.getDefaultToolkit().createImage(imageByte); - mBlankCursor = Toolkit.getDefaultToolkit() - .createCustomCursor(cursorImage, new Point(0, 0), "cursor"); - - setTooltip(false); - + mBlankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), "cursor"); revalidate(); } @@ -142,26 +128,6 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor } } - private void setTooltip(boolean locked) - { - String tooltip = null; - - if(locked) - { - tooltip = "Frequency control is locked. Disable decoding channels to unlock."; - } - else - { - tooltip = "Click on a frequency digit and type a frequency value, or click the upper/lower digits to " + - "change the value"; - } - - for(Digit digit: mDigits.values()) - { - digit.setToolTipText(tooltip); - } - } - /** * Receives a frequency change event invoked by another control. We don't * rebroadcast this event, just set the control to indicate the new frequency. @@ -172,36 +138,7 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor switch(event.getEvent()) { case NOTIFICATION_FREQUENCY_CHANGE: - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - setFrequency(event.getValue().longValue(), false); - } - }); - break; - case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED: - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - setEnabled(false); - setTooltip(true); - } - }); - break; - case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED: - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - setEnabled(true); - setTooltip(false); - } - }); + EventQueue.invokeLater(() -> setFrequency(event.getValue().longValue(), false)); break; } } @@ -251,11 +188,27 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor } } + /** + * Adds the source event listener + * @param processor + */ public void addListener(ISourceEventProcessor processor) { mProcessors.add(processor); } + /** + * Remove all source event processors + */ + public void clearListeners() + { + mProcessors.clear(); + } + + /** + * Removes the source event processor + * @param processor + */ public void removeListener(ISourceEventProcessor processor) { mProcessors.remove(processor); @@ -272,7 +225,7 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor super("0"); mPower = position; - + setToolTipText(getTooltip(mPower)); Listener listener = new Listener(); this.addKeyListener(listener); @@ -280,6 +233,49 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor this.addMouseWheelListener(listener); } + private static String getTooltip(int power) + { + StringBuilder sb = new StringBuilder(); + sb.append("Center frequency "); + switch(power) + { + case 0: + sb.append("1 Hertz"); + break; + case 1: + sb.append("10 Hertz"); + break; + case 2: + sb.append("100 Hertz"); + break; + case 3: + sb.append("1 Kilohertz"); + break; + case 4: + sb.append("10 Kilohertz"); + break; + case 5: + sb.append("100 Kilohertz"); + break; + case 6: + sb.append("1 Megahertz"); + break; + case 7: + sb.append("10 Megahertz"); + break; + case 8: + sb.append("100 Megahertz"); + break; + case 9: + sb.append("1 Gigahertz"); + break; + } + + sb.append(" units. Type a number, use up/down arrows, or click the upper/lower digit control to adjust"); + + return sb.toString(); + } + /** * Sets this digit to the value of the column in frequency that corresponds * to the power (ie column) set for this digit. Optionally, fires a diff --git a/src/main/java/io/github/dsheirer/gui/editor/EmptyEditor.java b/src/main/java/io/github/dsheirer/gui/editor/EmptyEditor.java deleted file mode 100644 index c40eae05..00000000 --- a/src/main/java/io/github/dsheirer/gui/editor/EmptyEditor.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.dsheirer.gui.editor; - -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; - -public class EmptyEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - public EmptyEditor( String descriptor ) - { - setLayout( new MigLayout( "insets 0 0 0 0", "[center]", "[grow]" ) ); - add( new JLabel( "Please select " + descriptor ), "wrap" ); - } - - public EmptyEditor() - { - this( "an item" ); - } - - @Override - public void save() - { - } - - @Override - public void reset() - { - } -} diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/DecoderPaneFactory.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/DecoderPaneFactory.java index 15d4a195..b9656872 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/DecoderPaneFactory.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/DecoderPaneFactory.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.gui.instrument.decoder; diff --git a/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditor.java index b9614cf5..b8358b57 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditor.java @@ -32,6 +32,7 @@ import io.github.dsheirer.gui.preference.PreferenceEditorType; import io.github.dsheirer.gui.preference.ViewUserPreferenceEditorRequest; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.util.ThreadPool; import io.github.dsheirer.util.TimeStamp; import javafx.application.Platform; @@ -65,6 +66,7 @@ public class PlaylistEditor extends BorderPane private static final Logger mLog = LoggerFactory.getLogger(PlaylistEditor.class); private PlaylistManager mPlaylistManager; + private TunerManager mTunerManager; private UserPreferences mUserPreferences; private MenuBar mMenuBar; private TabPane mTabPane; @@ -79,11 +81,13 @@ public class PlaylistEditor extends BorderPane /** * Constructs an instance * @param playlistManager for alias and channel models + * @param tunerManager for tuners * @param userPreferences for settings */ - public PlaylistEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public PlaylistEditor(PlaylistManager playlistManager, TunerManager tunerManager, UserPreferences userPreferences) { mPlaylistManager = playlistManager; + mTunerManager = tunerManager; mUserPreferences = userPreferences; //Throw a new runnable back onto the FX thread to lazy load the editor content after the editor has been @@ -240,7 +244,7 @@ public class PlaylistEditor extends BorderPane { if(mChannelEditor == null) { - mChannelEditor = new ChannelEditor(mPlaylistManager, mUserPreferences); + mChannelEditor = new ChannelEditor(mPlaylistManager, mTunerManager, mUserPreferences); } return mChannelEditor; diff --git a/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditorApplication.java b/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditorApplication.java index c804b278..b899d95e 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditorApplication.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/PlaylistEditorApplication.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,17 +20,12 @@ package io.github.dsheirer.gui.playlist; import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.audio.broadcast.BroadcastModel; -import io.github.dsheirer.controller.channel.map.ChannelMapModel; import io.github.dsheirer.gui.JavaFxWindowManager; import io.github.dsheirer.icon.IconModel; import io.github.dsheirer.module.log.EventLogManager; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.SourceManager; -import io.github.dsheirer.source.tuner.TunerModel; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.application.Application; import javafx.scene.Parent; import javafx.scene.Scene; @@ -43,6 +38,7 @@ public class PlaylistEditorApplication extends Application { private Stage mStage; private Parent mPlaylistEditor; + private TunerManager mTunerManager; private UserPreferences mUserPreferences = new UserPreferences(); private PlaylistManager mPlaylistManager; private JavaFxWindowManager mJavaFxWindowManager; @@ -50,17 +46,13 @@ public class PlaylistEditorApplication extends Application public PlaylistEditorApplication() { AliasModel aliasModel = new AliasModel(); - BroadcastModel broadcastModel = new BroadcastModel(aliasModel, new IconModel(), mUserPreferences); - ChannelMapModel channelMapModel = new ChannelMapModel(); - TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel(); - TunerModel tunerModel = new TunerModel(tunerConfigurationModel); - SourceManager sourceManager = new SourceManager(tunerModel, new SettingsManager(tunerConfigurationModel), - mUserPreferences); EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences); - mPlaylistManager = new PlaylistManager(mUserPreferences, sourceManager, aliasModel, eventLogManager, new IconModel()); + mTunerManager = new TunerManager(mUserPreferences); + mTunerManager.start(); + mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, new IconModel()); mPlaylistManager.init(); - mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mPlaylistManager); + mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mTunerManager, mPlaylistManager); } @Override @@ -77,7 +69,7 @@ public class PlaylistEditorApplication extends Application { if(mPlaylistEditor == null) { - mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mUserPreferences); + mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mTunerManager, mUserPreferences); } return mPlaylistEditor; diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/UnrecognizedActionEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/UnrecognizedActionEditor.java index 3e482944..9d087d56 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/UnrecognizedActionEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/UnrecognizedActionEditor.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnrecognizedIdentifierEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnrecognizedIdentifierEditor.java index 59dd3791..32e9c025 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnrecognizedIdentifierEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnrecognizedIdentifierEditor.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java index 7a8fe157..98b09b38 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -34,6 +31,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.Label; @@ -59,11 +57,14 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public AMConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public AMConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getRecordPane()); @@ -138,7 +139,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel()); + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager); //Add a listener so that we can push change notifications up to this editor mSourceConfigurationEditor.modifiedProperty() diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java index 9ff926c5..12a8c193 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java @@ -36,7 +36,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfiguration; -import io.github.dsheirer.source.tuner.TunerModel; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.util.ThreadPool; import javafx.application.Platform; import javafx.beans.value.ChangeListener; @@ -80,6 +80,7 @@ public abstract class ChannelConfigurationEditor extends Editor private final static Logger mLog = LoggerFactory.getLogger(ChannelConfigurationEditor.class); private PlaylistManager mPlaylistManager; + protected TunerManager mTunerManager; protected UserPreferences mUserPreferences; protected EditorModificationListener mEditorModificationListener = new EditorModificationListener(); private Button mPlayButton; @@ -100,9 +101,17 @@ public abstract class ChannelConfigurationEditor extends Editor private IconNode mStopGraphicNode; private ChannelProcessingMonitor mChannelProcessingMonitor = new ChannelProcessingMonitor(); - public ChannelConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + /** + * Constructs an instance + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences + */ + public ChannelConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { mPlaylistManager = playlistManager; + mTunerManager = tunerManager; mUserPreferences = userPreferences; setMaxWidth(Double.MAX_VALUE); @@ -236,11 +245,6 @@ public abstract class ChannelConfigurationEditor extends Editor modifiedProperty().setValue(false); } - protected TunerModel getTunerModel() - { - return mPlaylistManager.getTunerModel(); - } - @Override public void save() { diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditorFactory.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditorFactory.java index e0924b65..d4992fe3 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditorFactory.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditorFactory.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -25,6 +22,7 @@ package io.github.dsheirer.gui.playlist.channel; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.manager.TunerManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,28 +44,28 @@ public class ChannelConfigurationEditorFactory * @return constructed editor */ public static ChannelConfigurationEditor getEditor(DecoderType decoderType, PlaylistManager playlistManager, - UserPreferences userPreferences) + TunerManager tunerManager, UserPreferences userPreferences) { switch(decoderType) { case AM: - return new AMConfigurationEditor(playlistManager, userPreferences); + return new AMConfigurationEditor(playlistManager, tunerManager, userPreferences); case DMR: - return new DMRConfigurationEditor(playlistManager, userPreferences); + return new DMRConfigurationEditor(playlistManager, tunerManager, userPreferences); case NBFM: - return new NBFMConfigurationEditor(playlistManager, userPreferences); + return new NBFMConfigurationEditor(playlistManager, tunerManager, userPreferences); case LTR_NET: - return new LTRNetConfigurationEditor(playlistManager, userPreferences); + return new LTRNetConfigurationEditor(playlistManager, tunerManager, userPreferences); case LTR: - return new LTRConfigurationEditor(playlistManager, userPreferences); + return new LTRConfigurationEditor(playlistManager, tunerManager, userPreferences); case MPT1327: - return new MPT1327ConfigurationEditor(playlistManager, userPreferences); + return new MPT1327ConfigurationEditor(playlistManager, tunerManager, userPreferences); case PASSPORT: - return new PassportConfigurationEditor(playlistManager, userPreferences); + return new PassportConfigurationEditor(playlistManager, tunerManager, userPreferences); case P25_PHASE1: - return new P25P1ConfigurationEditor(playlistManager, userPreferences); + return new P25P1ConfigurationEditor(playlistManager, tunerManager, userPreferences); case P25_PHASE2: - return new P25P2ConfigurationEditor(playlistManager, userPreferences); + return new P25P2ConfigurationEditor(playlistManager, tunerManager, userPreferences); default: if(decoderType != null && !mLoggedUnrecognizedTypes.contains(decoderType)) { diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java index 3c785544..7387f047 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -28,6 +25,7 @@ import io.github.dsheirer.module.decode.DecoderFactory; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableValue; @@ -79,6 +77,7 @@ public class ChannelEditor extends SplitPane { private final static Logger mLog = LoggerFactory.getLogger(ChannelEditor.class); private PlaylistManager mPlaylistManager; + private TunerManager mTunerManager; private UserPreferences mUserPreferences; private TableView mChannelTableView; private Label mPlaceholderLabel; @@ -102,11 +101,12 @@ public class ChannelEditor extends SplitPane * Constructs an instance * @param playlistManager containing playlists and channel configurations */ - public ChannelEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public ChannelEditor(PlaylistManager playlistManager, TunerManager tunerManager, UserPreferences userPreferences) { mPlaylistManager = playlistManager; + mTunerManager = tunerManager; mUserPreferences = userPreferences; - mUnknownConfigurationEditor = new UnknownConfigurationEditor(mPlaylistManager, userPreferences); + mUnknownConfigurationEditor = new UnknownConfigurationEditor(mPlaylistManager, mTunerManager, userPreferences); HBox channelsBox = new HBox(); channelsBox.setSpacing(10.0); @@ -203,7 +203,7 @@ public class ChannelEditor extends SplitPane if(editor == null) { editor = ChannelConfigurationEditorFactory.getEditor(channelDecoderType, mPlaylistManager, - mUserPreferences); + mTunerManager, mUserPreferences); if(editor != null) { diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java index 44886d27..dfe88f03 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -40,6 +37,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; @@ -96,11 +94,14 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public DMRConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public DMRConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getEventLogPane()); @@ -240,7 +241,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager, DecodeConfigDMR.CHANNEL_ROTATION_DELAY_MINIMUM_MS, DecodeConfigDMR.CHANNEL_ROTATION_DELAY_MAXIMUM_MS, DecodeConfigDMR.CHANNEL_ROTATION_DELAY_DEFAULT_MS); diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java index f593e851..c6b1fa09 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -39,6 +36,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.Label; @@ -74,12 +72,14 @@ public class LTRConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager - * @param userPreferences + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public LTRConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public LTRConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getAuxDecoderPane()); @@ -210,7 +210,7 @@ public class LTRConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel()); + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager); //Add a listener so that we can push change notifications up to this editor mSourceConfigurationEditor.modifiedProperty() diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java index bb462eb9..7204187f 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -39,6 +36,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.Label; @@ -74,12 +72,13 @@ public class LTRNetConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager - * @param userPreferences + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public LTRNetConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public LTRNetConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getAuxDecoderPane()); @@ -210,7 +209,7 @@ public class LTRNetConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel()); + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager); //Add a listener so that we can push change notifications up to this editor mSourceConfigurationEditor.modifiedProperty() diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java index 485ff35b..79658d5c 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -40,6 +37,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.HPos; @@ -84,12 +82,14 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager - * @param userPreferences + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public MPT1327ConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public MPT1327ConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getEventLogPane()); @@ -195,7 +195,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager, DecodeConfigMPT1327.CHANNEL_ROTATION_DELAY_MINIMUM_MS, DecodeConfigMPT1327.CHANNEL_ROTATION_DELAY_MAXIMUM_MS, DecodeConfigMPT1327.CHANNEL_ROTATION_DELAY_DEFAULT_MS); diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java index 08ec4145..4dfe1dae 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -42,6 +39,7 @@ import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; @@ -92,12 +90,14 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager - * @param userPreferences + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public NBFMConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public NBFMConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getAuxDecoderPane()); @@ -228,7 +228,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel()); + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager); //Add a listener so that we can push change notifications up to this editor mSourceConfigurationEditor.modifiedProperty() diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java index fb6d8c40..5fac522a 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -38,6 +35,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.HPos; @@ -80,12 +78,14 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor /** * Constructs an instance - * @param playlistManager - * @param userPreferences + * @param playlistManager for playlists + * @param tunerManager for tuners + * @param userPreferences for preferences */ - public P25P1ConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public P25P1ConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getEventLogPane()); @@ -191,7 +191,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager, DecodeConfigP25Phase1.CHANNEL_ROTATION_DELAY_MINIMUM_MS, DecodeConfigP25Phase1.CHANNEL_ROTATION_DELAY_MAXIMUM_MS, DecodeConfigP25Phase1.CHANNEL_ROTATION_DELAY_DEFAULT_MS); diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java index 827ca426..54bc3770 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -39,6 +36,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.Label; @@ -73,9 +71,10 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor * @param playlistManager * @param userPreferences */ - public P25P2ConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public P25P2ConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getEventLogPane()); @@ -179,7 +178,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel()); + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager); //Add a listener so that we can push change notifications up to this editor mSourceConfigurationEditor.modifiedProperty() diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/PassportConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/PassportConfigurationEditor.java index 42765f18..a95f5dd7 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/PassportConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/PassportConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -38,6 +35,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderType; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.geometry.Insets; import javafx.scene.control.Label; import javafx.scene.control.TitledPane; @@ -69,9 +67,10 @@ public class PassportConfigurationEditor extends ChannelConfigurationEditor * @param playlistManager * @param userPreferences */ - public PassportConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public PassportConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getSourcePane()); getTitledPanesBox().getChildren().add(getDecoderPane()); getTitledPanesBox().getChildren().add(getAuxDecoderPane()); @@ -155,7 +154,7 @@ public class PassportConfigurationEditor extends ChannelConfigurationEditor { if(mSourceConfigurationEditor == null) { - mSourceConfigurationEditor = new FrequencyEditor(getTunerModel()); + mSourceConfigurationEditor = new FrequencyEditor(mTunerManager); //Add a listener so that we can push change notifications up to this editor mSourceConfigurationEditor.modifiedProperty() diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/UnknownConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/UnknownConfigurationEditor.java index 0ae1d0d8..5d661aff 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/UnknownConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/UnknownConfigurationEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.channel; @@ -30,6 +27,7 @@ import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.config.RecordConfiguration; import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.scene.control.TitledPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,9 +44,10 @@ public class UnknownConfigurationEditor extends ChannelConfigurationEditor * Constructs an instance * @param aliasModel */ - public UnknownConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences) + public UnknownConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager, + UserPreferences userPreferences) { - super(playlistManager, userPreferences); + super(playlistManager, tunerManager, userPreferences); getTitledPanesBox().getChildren().add(getDecoderPane()); } diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/ModeDecoderType.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/ModeDecoderType.java index 80723b79..4d987322 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/ModeDecoderType.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/ModeDecoderType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceDecoder.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceDecoder.java index 23f89afe..9027d967 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceDecoder.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceDecoder.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java index 70dcaeb3..e3753102 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.source; @@ -25,8 +22,8 @@ package io.github.dsheirer.gui.playlist.source; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; import io.github.dsheirer.source.config.SourceConfiguration; -import io.github.dsheirer.source.tuner.TunerModel; import io.github.dsheirer.source.tuner.channel.rotation.ChannelRotationMonitor; +import io.github.dsheirer.source.tuner.manager.TunerManager; import javafx.beans.binding.Bindings; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; @@ -59,7 +56,7 @@ public class FrequencyEditor extends SourceConfigurationEditor mFrequencyBoxes = FXCollections.observableArrayList(); private ComboBox mPreferredTunerComboBox; @@ -75,13 +72,13 @@ public class FrequencyEditor extends SourceConfigurationEditor - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.gui.playlist.streaming; diff --git a/src/main/java/io/github/dsheirer/gui/preference/ViewUserPreferenceEditorRequest.java b/src/main/java/io/github/dsheirer/gui/preference/ViewUserPreferenceEditorRequest.java index 98452abf..d5860e17 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/ViewUserPreferenceEditorRequest.java +++ b/src/main/java/io/github/dsheirer/gui/preference/ViewUserPreferenceEditorRequest.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.gui.preference; diff --git a/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java index 2a5c7e7e..232533f3 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.gui.preference.directory; @@ -63,6 +62,11 @@ public class DirectoryPreferenceEditor extends HBox private Button mResetApplicationLogsButton; private Label mApplicationLogsPathLabel; + private Label mConfigurationLabel; + private Button mChangeConfigurationButton; + private Button mResetConfigurationButton; + private Label mConfigurationPathLabel; + private Label mEventLogsLabel; private Button mChangeEventLogsButton; private Button mResetEventLogsButton; @@ -154,6 +158,19 @@ public class DirectoryPreferenceEditor extends HBox mEditorPane.add(getResetApplicationLogsButton(), 3, row++); + GridPane.setMargin(getConfigurationLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getConfigurationLabel(), 0, row); + + GridPane.setMargin(getConfigurationPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getConfigurationPathLabel(), 1, row); + + GridPane.setMargin(getChangeConfigurationButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeConfigurationButton(), 2, row); + + GridPane.setMargin(getResetConfigurationButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetConfigurationButton(), 3, row++); + + GridPane.setMargin(getEventLogsLabel(), new Insets(0, 10, 0, 0)); mEditorPane.add(getEventLogsLabel(), 0, row); @@ -365,6 +382,71 @@ public class DirectoryPreferenceEditor extends HBox return mApplicationLogsPathLabel; } + private Label getConfigurationLabel() + { + if(mConfigurationLabel == null) + { + mConfigurationLabel = new Label("Configuration"); + } + + return mConfigurationLabel; + } + + private Button getChangeConfigurationButton() + { + if(mChangeConfigurationButton == null) + { + mChangeConfigurationButton = new Button("Change..."); + mChangeConfigurationButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Configuration Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryConfiguration().toFile()); + Stage stage = (Stage)getChangeConfigurationButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryConfiguration(selected.toPath()); + } + } + }); + } + + return mChangeConfigurationButton; + } + + private Button getResetConfigurationButton() + { + if(mResetConfigurationButton == null) + { + mResetConfigurationButton = new Button("Reset"); + mResetConfigurationButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryConfiguration(); + } + }); + } + + return mResetConfigurationButton; + } + + private Label getConfigurationPathLabel() + { + if(mConfigurationPathLabel == null) + { + mConfigurationPathLabel = new Label(mDirectoryPreference.getDirectoryConfiguration().toString()); + } + + return mConfigurationPathLabel; + } + private Label getEventLogsLabel() { if(mEventLogsLabel == null) diff --git a/src/main/java/io/github/dsheirer/identifier/Form.java b/src/main/java/io/github/dsheirer/identifier/Form.java index 17637796..979c1e23 100644 --- a/src/main/java/io/github/dsheirer/identifier/Form.java +++ b/src/main/java/io/github/dsheirer/identifier/Form.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.identifier; diff --git a/src/main/java/io/github/dsheirer/identifier/Identifier.java b/src/main/java/io/github/dsheirer/identifier/Identifier.java index 928bb349..324ed6a0 100644 --- a/src/main/java/io/github/dsheirer/identifier/Identifier.java +++ b/src/main/java/io/github/dsheirer/identifier/Identifier.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.identifier; import io.github.dsheirer.protocol.Protocol; diff --git a/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java b/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java index c0ff2bc2..51e54e49 100644 --- a/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java +++ b/src/main/java/io/github/dsheirer/identifier/IdentifierClass.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.identifier; /** diff --git a/src/main/java/io/github/dsheirer/identifier/tone/AmbeTone.java b/src/main/java/io/github/dsheirer/identifier/tone/AmbeTone.java index c9e2a307..62f9b809 100644 --- a/src/main/java/io/github/dsheirer/identifier/tone/AmbeTone.java +++ b/src/main/java/io/github/dsheirer/identifier/tone/AmbeTone.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/Decoder.java b/src/main/java/io/github/dsheirer/module/decode/Decoder.java index f90acfe1..0364e3f0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/Decoder.java @@ -1,20 +1,21 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.module.decode; import io.github.dsheirer.message.IMessage; diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java index 770090f3..27c47a91 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderType.java b/src/main/java/io/github/dsheirer/module/decode/DecoderType.java index 31690872..118632cd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderType.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderType.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java index 74d7f450..49d4bfc6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.dmr; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/UnknownDMRMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/UnknownDMRMessage.java index d9b7a822..81afa0ea 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/UnknownDMRMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/UnknownDMRMessage.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DataMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DataMessage.java index 3f19f091..79b2581b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DataMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DataMessage.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2020 Zhenyu Mao + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.dmr.message.data; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/SlotType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/SlotType.java index 8a7bd80d..26238a4d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/SlotType.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/SlotType.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2020 Zhenyu Mao + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.dmr.message.data; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraAnnouncement.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraAnnouncement.java index fb3a9ebd..25338d93 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraAnnouncement.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraAnnouncement.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Protect.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Protect.java index 985aa6b5..1a0aa2e2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Protect.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Protect.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/acknowledge/Acknowledge.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/acknowledge/Acknowledge.java index a944078f..1957f44c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/acknowledge/Acknowledge.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/acknowledge/Acknowledge.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/ahoy/ServiceRadioCheck.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/ahoy/ServiceRadioCheck.java index 2572b7cd..fc6b5a4f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/ahoy/ServiceRadioCheck.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/ahoy/ServiceRadioCheck.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/Announcement.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/Announcement.java index 0826e3bd..f05fc1c7 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/Announcement.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/Announcement.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/PacketSequenceHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/PacketSequenceHeader.java index b862343c..72f3a502 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/PacketSequenceHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/PacketSequenceHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/hytera/HyteraProprietaryDataHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/hytera/HyteraProprietaryDataHeader.java index 905108b6..0c20646e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/hytera/HyteraProprietaryDataHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/hytera/HyteraProprietaryDataHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java index a0dd66cf..523b5683 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/usb/USBData.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/usb/USBData.java index b788e1a6..24bbb4eb 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/usb/USBData.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/usb/USBData.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AcknowledgeType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AcknowledgeType.java index 09e2ca74..0f6d581f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AcknowledgeType.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AcknowledgeType.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message.type; /** diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AnnouncementType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AnnouncementType.java index 8c376efe..128f2227 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AnnouncementType.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/AnnouncementType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ApplicationType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ApplicationType.java index e62024b7..203a2298 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ApplicationType.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ApplicationType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Gateway.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Gateway.java index b8a17f9a..bd94ada1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Gateway.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Gateway.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message.type; /** diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Model.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Model.java index b6043485..c1cc8715 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Model.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Model.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PAR.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PAR.java index caaa687a..4d079ba3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PAR.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PAR.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceFunction.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceFunction.java index 7f38e6da..698e6198 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceFunction.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceFunction.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceKind.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceKind.java index 5a7b0dd9..04741003 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceKind.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceKind.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message.type; /** diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceType.java index d0c634d2..511224be 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceType.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/ServiceType.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message.type; /** diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Version.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Version.java index 2f659223..d36db928 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Version.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/Version.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java index 72cc3920..0321032e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.event; diff --git a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java index 12a3703f..8fb84d6b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.event; diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java index 8aeda15c..3de848dd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/ars/ARSHeader.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.ip.ars; diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPHeader.java index 5e1aa51d..8e177f09 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPHeader.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.ip.cellocator; diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageFactory.java index 75f71455..dc2cd47d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageFactory.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.ip.cellocator; diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageType.java b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageType.java index c50dc14b..44d30da0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageType.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/MCGPMessageType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularRequestMessage.java b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularRequestMessage.java index 0abb11f9..9f3941d1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularRequestMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularRequestMessage.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularResponseMessage.java b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularResponseMessage.java index 4a0cd6be..6df2c838 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularResponseMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/cellocator/ModularResponseMessage.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/icmp/ICMPTypeCode.java b/src/main/java/io/github/dsheirer/module/decode/ip/icmp/ICMPTypeCode.java index c759a90a..a01672c0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/icmp/ICMPTypeCode.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/icmp/ICMPTypeCode.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPHeader.java index 55fd288b..d081d690 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java index 1b3a1a9a..d549595d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java index 0e3c8787..028fbfbd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,6 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; import com.google.common.base.Joiner; -import io.github.dsheirer.bits.CorrectedBinaryMessage; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java index 01e4d84a..d12ca25c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPHeader.java index 0b0b64c1..22d8d943 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPMessageType.java b/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPMessageType.java index 03defd80..85a9cde0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPMessageType.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/xcmp/XCMPMessageType.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java index 3771e7b0..c1752ffd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/isw/LtrNetIswMessage.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.ltrnet.message.isw; diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRMessage.java index d784c02f..1495a490 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/LTRMessage.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.ltrstandard.message; diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java index 0c5ee011..16cc4c65 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/message/UnknownMessage.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.ltrstandard.message; diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java index 4e4cd50a..6cdf8018 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.mpt1327; diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java index 87abb4d2..661421bc 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Message.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.mpt1327; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25P2AudioModule.java b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25P2AudioModule.java index a1aae512..28d40449 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25P2AudioModule.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25P2AudioModule.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DataUnitID.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DataUnitID.java index cd6a26be..559642fd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DataUnitID.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DataUnitID.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java index fb2f680f..1ab280fd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1NetworkConfigurationMonitor.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1NetworkConfigurationMonitor.java index 6515b9a5..2739c957 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1NetworkConfigurationMonitor.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1NetworkConfigurationMonitor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1; @@ -52,8 +49,12 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; -import java.util.function.Consumer; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; /** * Tracks the network configuration details of a P25 Phase 1 network from the broadcast messages diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/IFrequencyBandReceiver.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/IFrequencyBandReceiver.java index ed2effd7..131370be 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/IFrequencyBandReceiver.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/IFrequencyBandReceiver.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25Message.java index 6cbde82c..8628075b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25Message.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25MessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25MessageFactory.java index ccce5d77..6958bd61 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25MessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/P25MessageFactory.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/standard/LCExtendedFunctionCommand.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/standard/LCExtendedFunctionCommand.java index 022e805b..cfabb6f8 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/standard/LCExtendedFunctionCommand.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/standard/LCExtendedFunctionCommand.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.lc.standard; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/ActivateTdsContextRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/ActivateTdsContextRequest.java index 644259f3..3ce5cb3b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/ActivateTdsContextRequest.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/ActivateTdsContextRequest.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessage.java index 6aa560c9..1814cd2c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessage.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessageFactory.java index 51a42a18..4deb8015 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/packet/sndcp/SNDCPMessageFactory.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/response/ResponseMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/response/ResponseMessage.java index 81482be8..706efeee 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/response/ResponseMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/pdu/response/ResponseMessage.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.pdu.response; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/motorola/osp/MotorolaDenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/motorola/osp/MotorolaDenyResponse.java index c4a949ee..8ca5ccb1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/motorola/osp/MotorolaDenyResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/motorola/osp/MotorolaDenyResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.motorola.osp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/CancelServiceRequest.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/CancelServiceRequest.java index 9051b7e0..74534174 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/CancelServiceRequest.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/CancelServiceRequest.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.standard.isp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/UnitAcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/UnitAcknowledgeResponse.java index 20584b74..9de2fbfa 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/UnitAcknowledgeResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/isp/UnitAcknowledgeResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.standard.isp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/AcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/AcknowledgeResponse.java index 814e57d0..b2935ad2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/AcknowledgeResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/AcknowledgeResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.standard.osp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/DenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/DenyResponse.java index a7d089e0..790da4da 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/DenyResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/DenyResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.standard.osp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/QueuedResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/QueuedResponse.java index 0389f1f6..8fbbdd7f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/QueuedResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/standard/osp/QueuedResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.standard.osp; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2MessageProcessor.java index 6228fc7c..314f0ae5 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2MessageProcessor.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase2; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacMessage.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacMessage.java index 0b436092..ebdb9b69 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacMessage.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase2.message.mac; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacPduType.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacPduType.java index 5e63c662..244f99f5 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacPduType.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacPduType.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase2.message.mac; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/AcknowledgeResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/AcknowledgeResponse.java index e3a408bd..4259899d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/AcknowledgeResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/AcknowledgeResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/DenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/DenyResponse.java index d039d3cd..2a72916e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/DenyResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/DenyResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/QueuedResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/QueuedResponse.java index 38fb2d47..f6c7a746 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/QueuedResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/QueuedResponse.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure; diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogType.java b/src/main/java/io/github/dsheirer/module/log/EventLogType.java index 51f2d3c6..35127dd5 100644 --- a/src/main/java/io/github/dsheirer/module/log/EventLogType.java +++ b/src/main/java/io/github/dsheirer/module/log/EventLogType.java @@ -1,20 +1,21 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.module.log; diff --git a/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java b/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java index 080c38b3..95c7c7ce 100644 --- a/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java +++ b/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.playlist; @@ -41,8 +38,7 @@ import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.playlist.PlaylistPreference; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.service.radioreference.RadioReference; -import io.github.dsheirer.source.SourceManager; -import io.github.dsheirer.source.tuner.TunerModel; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.util.ThreadPool; import javafx.collections.ListChangeListener; import org.slf4j.Logger; @@ -75,7 +71,7 @@ public class PlaylistManager implements Listener private BroadcastModel mBroadcastModel; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; - private SourceManager mSourceManager; + private TunerManager mTunerManager; private UserPreferences mUserPreferences; private RadioReference mRadioReference; private AtomicBoolean mPlaylistSavePending = new AtomicBoolean(); @@ -89,14 +85,16 @@ public class PlaylistManager implements Listener * Monitors playlist changes to automatically save configuration changes after they occur. * * @param userPreferences for user settings - * @param sourceManager for access to tuner model + * @param tunerManager for access to tuner model + * @param aliasModel for aliases * @param eventLogManager for event logging + * @param iconModel for icons */ - public PlaylistManager(UserPreferences userPreferences, SourceManager sourceManager, AliasModel aliasModel, + public PlaylistManager(UserPreferences userPreferences, TunerManager tunerManager, AliasModel aliasModel, EventLogManager eventLogManager, IconModel iconModel) { mUserPreferences = userPreferences; - mSourceManager = sourceManager; + mTunerManager = tunerManager; mAliasModel = aliasModel; mIconModel = iconModel; @@ -104,7 +102,7 @@ public class PlaylistManager implements Listener mRadioReference = new RadioReference(mUserPreferences); mChannelModel = new ChannelModel(mAliasModel); - mChannelProcessingManager = new ChannelProcessingManager(mChannelMapModel, eventLogManager, mSourceManager, + mChannelProcessingManager = new ChannelProcessingManager(mChannelMapModel, eventLogManager, mTunerManager, mAliasModel, mUserPreferences); mChannelModel.addListener(mChannelProcessingManager); mChannelProcessingManager.addChannelEventListener(mChannelModel); @@ -188,14 +186,6 @@ public class PlaylistManager implements Listener return mChannelMapModel; } - /** - * Tuner model for this playlist manager - */ - public TunerModel getTunerModel() - { - return mSourceManager.getTunerModel(); - } - /** * Loads playlist from the current playlist file, or the default playlist file, * as specified in the current SDRTRunk system settings diff --git a/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java b/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java index e51fafb0..735ff17d 100644 --- a/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java +++ b/src/main/java/io/github/dsheirer/playlist/PlaylistUpdater.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.playlist; diff --git a/src/main/java/io/github/dsheirer/preference/Preference.java b/src/main/java/io/github/dsheirer/preference/Preference.java index b6003d24..a90522fc 100644 --- a/src/main/java/io/github/dsheirer/preference/Preference.java +++ b/src/main/java/io/github/dsheirer/preference/Preference.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.preference; diff --git a/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java b/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java index 9f4e2fac..3b6aca7e 100644 --- a/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java +++ b/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.preference.directory; @@ -41,6 +40,7 @@ public class DirectoryPreference extends Preference private static final String DIRECTORY_APPLICATION_ROOT = "SDRTrunk"; private static final String DIRECTORY_APPLICATION_LOG = "logs"; + private static final String DIRECTORY_CONFIGURATION = "configuration"; private static final String DIRECTORY_EVENT_LOG = "event_logs"; private static final String DIRECTORY_JMBE = "jmbe"; private static final String DIRECTORY_PLAYLIST = "playlist"; @@ -50,6 +50,7 @@ public class DirectoryPreference extends Preference private static final String PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT = "directory.application.root"; private static final String PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS = "directory.application.logs"; + private static final String PREFERENCE_KEY_DIRECTORY_CONFIGURATION = "directory.configuration"; private static final String PREFERENCE_KEY_DIRECTORY_EVENT_LOGS = "directory.event.logs"; private static final String PREFERENCE_KEY_DIRECTORY_JMBE = "directory.jmbe"; private static final String PREFERENCE_KEY_DIRECTORY_PLAYLIST = "directory.playlist"; @@ -59,6 +60,7 @@ public class DirectoryPreference extends Preference private Path mDirectoryApplicationRoot; private Path mDirectoryApplicationLogs; + private Path mDirectoryConfiguration; private Path mDirectoryEventLogs; private Path mDirectoryJmbe; private Path mDirectoryPlaylist; @@ -174,6 +176,40 @@ public class DirectoryPreference extends Preference notifyPreferenceUpdated(); } + /** + * Path to the folder for storing configuration files + */ + public Path getDirectoryConfiguration() + { + if(mDirectoryConfiguration == null) + { + mDirectoryConfiguration = getPath(PREFERENCE_KEY_DIRECTORY_CONFIGURATION, getDefaultConfigurationDirectory()); + createDirectory(mDirectoryConfiguration); + } + + return mDirectoryConfiguration; + } + + /** + * Sets the path to the configuration files folder + */ + public void setDirectoryConfiguration(Path path) + { + mDirectoryConfiguration = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_CONFIGURATION, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored configuration directory preference so that the default path can be used again + */ + public void resetDirectoryConfiguration() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_CONFIGURATION); + mDirectoryConfiguration = null; + notifyPreferenceUpdated(); + } + /** * Path to the folder for storing event logs */ @@ -199,7 +235,7 @@ public class DirectoryPreference extends Preference } /** - * Removes a stored playlist directory preference so that the default path can be used again + * Removes a stored event logs directory preference so that the default path can be used again */ public void resetDirectoryEventLogs() { @@ -394,6 +430,14 @@ public class DirectoryPreference extends Preference return getDirectoryApplicationRoot().resolve(DIRECTORY_APPLICATION_LOG); } + /** + * Default configuration directory + */ + public Path getDefaultConfigurationDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_CONFIGURATION); + } + /** * Default event logs directory */ diff --git a/src/main/java/io/github/dsheirer/preference/source/ChannelizerType.java b/src/main/java/io/github/dsheirer/preference/source/ChannelizerType.java index 69d5a11d..c0f283d4 100644 --- a/src/main/java/io/github/dsheirer/preference/source/ChannelizerType.java +++ b/src/main/java/io/github/dsheirer/preference/source/ChannelizerType.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.preference.source; diff --git a/src/main/java/io/github/dsheirer/preference/source/TunerPreference.java b/src/main/java/io/github/dsheirer/preference/source/TunerPreference.java index fcd13d93..94d40e4c 100644 --- a/src/main/java/io/github/dsheirer/preference/source/TunerPreference.java +++ b/src/main/java/io/github/dsheirer/preference/source/TunerPreference.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.preference.source; diff --git a/src/main/java/io/github/dsheirer/record/RecorderFactory.java b/src/main/java/io/github/dsheirer/record/RecorderFactory.java index 42a69a3b..ef8364cc 100644 --- a/src/main/java/io/github/dsheirer/record/RecorderFactory.java +++ b/src/main/java/io/github/dsheirer/record/RecorderFactory.java @@ -27,6 +27,7 @@ import io.github.dsheirer.module.decode.p25.audio.P25P2CallSequenceRecorder; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.binary.BinaryRecorder; import io.github.dsheirer.record.wave.ComplexSamplesWaveRecorder; +import io.github.dsheirer.record.wave.IRecordingStatusListener; import io.github.dsheirer.record.wave.NativeBufferWaveRecorder; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; @@ -171,12 +172,12 @@ public class RecorderFactory /** * Constructs a baseband recorder for use in a processing chain. */ - public static NativeBufferWaveRecorder getTunerRecorder(String channelName, UserPreferences userPreferences) + public static NativeBufferWaveRecorder getTunerRecorder(String channelName, UserPreferences userPreferences, + IRecordingStatusListener statusListener) { StringBuilder sb = new StringBuilder(); sb.append(getRecordingBasePath(userPreferences)); sb.append(File.separator).append(StringUtils.replaceIllegalCharacters(channelName)).append("_baseband"); - - return new NativeBufferWaveRecorder(BASEBAND_SAMPLE_RATE, sb.toString()); + return new NativeBufferWaveRecorder(BASEBAND_SAMPLE_RATE, sb.toString(), statusListener); } } diff --git a/src/main/java/io/github/dsheirer/record/config/RecordConfiguration.java b/src/main/java/io/github/dsheirer/record/config/RecordConfiguration.java index 482687e0..96f24707 100644 --- a/src/main/java/io/github/dsheirer/record/config/RecordConfiguration.java +++ b/src/main/java/io/github/dsheirer/record/config/RecordConfiguration.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.record.config; diff --git a/src/main/java/io/github/dsheirer/record/wave/AudioMetadata.java b/src/main/java/io/github/dsheirer/record/wave/AudioMetadata.java index 004bb28d..2b69d2dc 100644 --- a/src/main/java/io/github/dsheirer/record/wave/AudioMetadata.java +++ b/src/main/java/io/github/dsheirer/record/wave/AudioMetadata.java @@ -1,18 +1,20 @@ /* - * ********************************************************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2017 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * ********************************************************************************************************************* + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.record.wave; diff --git a/src/main/java/io/github/dsheirer/record/wave/AudioMetadataUtils.java b/src/main/java/io/github/dsheirer/record/wave/AudioMetadataUtils.java index 6da95f9f..50efa046 100644 --- a/src/main/java/io/github/dsheirer/record/wave/AudioMetadataUtils.java +++ b/src/main/java/io/github/dsheirer/record/wave/AudioMetadataUtils.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.record.wave; @@ -43,7 +42,11 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; public class AudioMetadataUtils { diff --git a/src/main/java/io/github/dsheirer/record/wave/IRecordingStatusListener.java b/src/main/java/io/github/dsheirer/record/wave/IRecordingStatusListener.java new file mode 100644 index 00000000..a102e0d8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/record/wave/IRecordingStatusListener.java @@ -0,0 +1,34 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.record.wave; + +/** + * Listener interface for receiving real-time progress updates on a recording. + */ +public interface IRecordingStatusListener +{ + /** + * Updates the listener on the file name and the current size of the recording + * @param fileCount recording number + * @param file path + * @param size of recording + */ + void update(int fileCount, String file, long size); +} diff --git a/src/main/java/io/github/dsheirer/record/wave/NativeBufferWaveRecorder.java b/src/main/java/io/github/dsheirer/record/wave/NativeBufferWaveRecorder.java index 69484f02..997a4805 100644 --- a/src/main/java/io/github/dsheirer/record/wave/NativeBufferWaveRecorder.java +++ b/src/main/java/io/github/dsheirer/record/wave/NativeBufferWaveRecorder.java @@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory; import javax.sound.sampled.AudioFormat; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Iterator; @@ -44,20 +45,26 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class NativeBufferWaveRecorder extends Module implements Listener, ISourceEventListener { - private final static Logger mLog = LoggerFactory.getLogger(ComplexSamplesWaveRecorder.class); - + private static final Logger mLog = LoggerFactory.getLogger(ComplexSamplesWaveRecorder.class); + private static final long STATUS_UPDATE_BYTE_INTERVAL = 1_048_576; + private static final long MAX_RECORDING_SIZE = (long)Integer.MAX_VALUE * 2l; private Dispatcher mBufferProcessor = new Dispatcher<>(500, - "sdrtrunk-native buffer wave recorder", new NativeBufferPoisonPill()); + "sdrtrunk native buffer wave recorder", new NativeBufferPoisonPill()); private AtomicBoolean mRunning = new AtomicBoolean(); - private INativeBufferWaveWriter mWriter; + private NativeBufferWaveWriter mWriter; private String mFilePrefix; - private Path mFile; private AudioFormat mAudioFormat; + private IRecordingStatusListener mStatusListener; + private String mFilePath; + private long mCurrentSize = 0; + private long mLastReportedSize = 0; + private int mRecordingCount = 0; - public NativeBufferWaveRecorder(float sampleRate, String filePrefix) + public NativeBufferWaveRecorder(float sampleRate, String filePrefix, IRecordingStatusListener statusListener) { mFilePrefix = filePrefix; + mStatusListener = statusListener; setSampleRate(sampleRate); } @@ -75,27 +82,30 @@ public class NativeBufferWaveRecorder extends Module implements Listener { - @Override - public void run() + try { - try - { - mWriter.close(); - } - catch(IOException ioe) - { - mLog.error("Error closing baseband I/Q recorder", ioe); - } + mWriter.close(); + } + catch(IOException ioe) + { + mLog.error("Error closing baseband I/Q recorder", ioe); } }); } @@ -141,8 +182,11 @@ public class NativeBufferWaveRecorder extends Module implements Listener getReusableComplexBufferListener() @@ -158,17 +202,13 @@ public class NativeBufferWaveRecorder extends Module implements Listener getSourceEventListener() { - return new Listener() + return sourceEvent -> { - @Override - public void receive(SourceEvent sourceEvent) + switch(sourceEvent.getEvent()) { - switch(sourceEvent.getEvent()) - { - case NOTIFICATION_SAMPLE_RATE_CHANGE: - setSampleRate(sourceEvent.getValue().floatValue()); - break; - } + case NOTIFICATION_SAMPLE_RATE_CHANGE: + setSampleRate(sourceEvent.getValue().floatValue()); + break; } }; } @@ -176,9 +216,9 @@ public class NativeBufferWaveRecorder extends Module implements Listener + public class NativeBufferWaveWriter extends WaveWriter implements Listener { - public INativeBufferWaveWriter(AudioFormat format, Path file) throws IOException + public NativeBufferWaveWriter(AudioFormat format, Path file) throws IOException { super(format, file); } @@ -194,11 +234,25 @@ public class NativeBufferWaveRecorder extends Module implements Listener MAX_RECORDING_SIZE) + { + rollRecording(); + } + + mWriter.writeData(data); + + mCurrentSize += data.array().length; + if(mCurrentSize > (mLastReportedSize + STATUS_UPDATE_BYTE_INTERVAL)) + { + mStatusListener.update(mRecordingCount, mFilePath, mCurrentSize); + mLastReportedSize = mCurrentSize; + } } catch(IOException ioe) { - mLog.error("IOException while writing I/Q buffers to wave recorder - stopping recorder", ioe); + mLog.error("I/O exception while writing I/Q buffers to wave recorder - stopping recorder", ioe); error = true; stop(); } diff --git a/src/main/java/io/github/dsheirer/settings/Settings.java b/src/main/java/io/github/dsheirer/settings/Settings.java index 8da930da..3630ea7c 100644 --- a/src/main/java/io/github/dsheirer/settings/Settings.java +++ b/src/main/java/io/github/dsheirer/settings/Settings.java @@ -1,18 +1,20 @@ /* - * ********************************************************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * ********************************************************************************************************************* + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.settings; @@ -26,6 +28,7 @@ import io.github.dsheirer.source.recording.RecordingConfiguration; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @JacksonXmlRootElement(localName = "SDRTrunk_settings") @@ -35,6 +38,7 @@ public class Settings private List mTunerConfiguration = new ArrayList(); + //No longer used private List mRecordingConfigurations = new ArrayList(); public Settings() @@ -44,22 +48,11 @@ public class Settings @JacksonXmlProperty(isAttribute = false, localName = "recording_configuration") public List getRecordingConfigurations() { - return mRecordingConfigurations; + return Collections.emptyList(); } - public void setRecordingConfigurations(ArrayList configs) + public void setRecordingConfigurations(List configs) { - mRecordingConfigurations = configs; - } - - public void addRecordingConfiguration(RecordingConfiguration config) - { - mRecordingConfigurations.add(config); - } - - public void removeRecordingConfiguration(RecordingConfiguration config) - { - mRecordingConfigurations.remove(config); } @JacksonXmlProperty(isAttribute = false, localName = "tuner_configuration") diff --git a/src/main/java/io/github/dsheirer/settings/SettingsManager.java b/src/main/java/io/github/dsheirer/settings/SettingsManager.java index 00ccf70e..c8f26a5c 100644 --- a/src/main/java/io/github/dsheirer/settings/SettingsManager.java +++ b/src/main/java/io/github/dsheirer/settings/SettingsManager.java @@ -1,18 +1,20 @@ /* - * ********************************************************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * ********************************************************************************************************************* + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.settings; @@ -22,15 +24,13 @@ import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.recording.RecordingConfiguration; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.util.ThreadPool; import org.jdesktop.swingx.mapviewer.GeoPosition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.awt.*; +import java.awt.Color; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -48,21 +48,15 @@ public class SettingsManager implements Listener private Settings mSettings = new Settings(); private List mListeners = new ArrayList<>(); - private TunerConfigurationModel mTunerConfigurationModel; private boolean mLoadingSettings = false; private AtomicBoolean mSettingsSavePending = new AtomicBoolean(); - public SettingsManager(TunerConfigurationModel tunerConfigurationModel) + public SettingsManager() { //TODO: move settings into a SettingsModel //and update this class to only provide loading, saving, and model //change detection producing a save. - mTunerConfigurationModel = tunerConfigurationModel; - - //Register for tuner config events so that we can save the settings - mTunerConfigurationModel.addListener(this); - init(); } @@ -203,23 +197,6 @@ public class SettingsManager implements Listener broadcastSettingChange(setting); } - public List getRecordingConfigurations() - { - return mSettings.getRecordingConfigurations(); - } - - public void addRecordingConfiguration(RecordingConfiguration config) - { - mSettings.addRecordingConfiguration(config); - scheduleSettingsSave(); - } - - public void removeRecordingConfiguration(RecordingConfiguration config) - { - mSettings.removeRecordingConfiguration(config); - scheduleSettingsSave(); - } - public MapViewSetting getMapViewSetting(String name, GeoPosition position, int zoom) { MapViewSetting loc = mSettings.getMapViewSetting(name); @@ -250,8 +227,6 @@ public class SettingsManager implements Listener private void save() { - saveTunerConfigurationModel(); - SystemProperties props = SystemProperties.getInstance(); Path settingsFolder = props.getApplicationFolder("settings"); @@ -319,31 +294,9 @@ public class SettingsManager implements Listener mSettings = new Settings(); } - loadTunerConfigurationModel(); - mLoadingSettings = false; } - private void loadTunerConfigurationModel() - { - if(mSettings != null) - { - mTunerConfigurationModel.clear(); - - mTunerConfigurationModel.addTunerConfigurations( - mSettings.getTunerConfigurations()); - } - } - - private void saveTunerConfigurationModel() - { - if(mSettings != null) - { - mSettings.setTunerConfigurations( - mTunerConfigurationModel.getTunerConfigurations()); - } - } - public void broadcastSettingChange(Setting setting) { Iterator it = mListeners.iterator(); diff --git a/src/main/java/io/github/dsheirer/source/Source.java b/src/main/java/io/github/dsheirer/source/Source.java index eebd0489..062dde98 100644 --- a/src/main/java/io/github/dsheirer/source/Source.java +++ b/src/main/java/io/github/dsheirer/source/Source.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.source; diff --git a/src/main/java/io/github/dsheirer/source/SourceEvent.java b/src/main/java/io/github/dsheirer/source/SourceEvent.java index df09ce47..3fcacd89 100644 --- a/src/main/java/io/github/dsheirer/source/SourceEvent.java +++ b/src/main/java/io/github/dsheirer/source/SourceEvent.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source; @@ -48,6 +45,7 @@ public class SourceEvent NOTIFICATION_SAMPLE_RATE_CHANGE, NOTIFICATION_SQUELCH_THRESHOLD, NOTIFICATION_STOP_SAMPLE_STREAM, + NOTIFICATION_TUNER_SHUTDOWN, NOTIFICATION_ERROR_STATE, REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, @@ -197,6 +195,15 @@ public class SourceEvent return new SourceEvent(Event.NOTIFICATION_ERROR_STATE, source, errorDescription); } + /** + * Notification that a tuner is shutting down and the source is therefore shutting down. + * @param source that is shutting down + */ + public static SourceEvent tunerShutdown(Source source) + { + return new SourceEvent(Event.NOTIFICATION_TUNER_SHUTDOWN, source); + } + /** * Creates a new locked state change event */ diff --git a/src/main/java/io/github/dsheirer/source/SourceManager.java b/src/main/java/io/github/dsheirer/source/SourceManager.java deleted file mode 100644 index a3174ef7..00000000 --- a/src/main/java/io/github/dsheirer/source/SourceManager.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * ***************************************************************************** - */ -package io.github.dsheirer.source; - -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.config.SourceConfigTuner; -import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; -import io.github.dsheirer.source.config.SourceConfiguration; -import io.github.dsheirer.source.mixer.MixerManager; -import io.github.dsheirer.source.recording.RecordingSourceManager; -import io.github.dsheirer.source.tuner.TunerManager; -import io.github.dsheirer.source.tuner.TunerModel; -import io.github.dsheirer.source.tuner.channel.ChannelSpecification; -import io.github.dsheirer.source.tuner.channel.MultiFrequencyTunerChannelSource; -import io.github.dsheirer.source.tuner.channel.TunerChannel; -import io.github.dsheirer.source.tuner.channel.TunerChannelSource; - -public class SourceManager -{ - private RecordingSourceManager mRecordingSourceManager; - private TunerManager mTunerManager; - private TunerModel mTunerModel; - - public SourceManager(TunerModel tunerModel, SettingsManager settingsManager, UserPreferences userPreferences) - { - mTunerModel = tunerModel; - mRecordingSourceManager = new RecordingSourceManager(settingsManager); - mTunerManager = new TunerManager(tunerModel, userPreferences); - - //TODO: change mixer & recording managers to be models and hand them - //in via the constructor. Perform loading outside of this class. - } - - /** - * Prepare for shutdown and release all tuners - */ - public void shutdown() - { - mTunerManager.releaseTuners(); - mTunerManager.dispose(); - } - - public RecordingSourceManager getRecordingSourceManager() - { - return mRecordingSourceManager; - } - - public TunerManager getTunerManager() - { - return mTunerManager; - } - - public TunerModel getTunerModel() - { - return mTunerModel; - } - - public Source getSource(SourceConfiguration config, ChannelSpecification channelSpecification) throws SourceException - { - Source retVal = null; - - switch(config.getSourceType()) - { - case MIXER: - retVal = MixerManager.getSource(config); - break; - case TUNER: - if(config instanceof SourceConfigTuner) - { - SourceConfigTuner sourceConfigTuner = (SourceConfigTuner)config; - TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth()); - String preferredTuner = sourceConfigTuner.getPreferredTuner(); - retVal = mTunerModel.getSource(tunerChannel, channelSpecification, preferredTuner); - } - break; - case TUNER_MULTIPLE_FREQUENCIES: - if(config instanceof SourceConfigTunerMultipleFrequency) - { - SourceConfigTunerMultipleFrequency sourceConfigTuner = (SourceConfigTunerMultipleFrequency)config; - TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth()); - String preferredTuner = sourceConfigTuner.getPreferredTuner(); - - Source source = mTunerModel.getSource(tunerChannel, channelSpecification, preferredTuner); - - if(source instanceof TunerChannelSource) - { - retVal = new MultiFrequencyTunerChannelSource(getTunerModel(), (TunerChannelSource)source, - sourceConfigTuner.getFrequencies(), channelSpecification, sourceConfigTuner.getPreferredTuner()); - } - } - break; - case RECORDING: - retVal = mRecordingSourceManager.getSource(config, channelSpecification); - break; - case NONE: - default: - break; - } - - return retVal; - } -} diff --git a/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java b/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java index b1425004..86a39c54 100644 --- a/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java +++ b/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java @@ -61,9 +61,9 @@ public class ComplexMixer * Sets the buffer size in bytes per buffer for each read interval. * @param bufferSize in bytes */ - public void setBufferSize(int bufferSize) + public void setBufferSampleCount(int bufferSize) { - mMixerReader.setBufferSize(bufferSize); + mMixerReader.setBufferSampleSize(bufferSize); } public void start() diff --git a/src/main/java/io/github/dsheirer/source/mixer/MixerReader.java b/src/main/java/io/github/dsheirer/source/mixer/MixerReader.java index 3c560828..ef298b0f 100644 --- a/src/main/java/io/github/dsheirer/source/mixer/MixerReader.java +++ b/src/main/java/io/github/dsheirer/source/mixer/MixerReader.java @@ -22,16 +22,12 @@ import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.adapter.ISampleAdapter; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.heartbeat.HeartbeatManager; -import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.TargetDataLine; -import java.util.Arrays; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -39,23 +35,26 @@ import java.util.concurrent.atomic.AtomicBoolean; * the samples to an array of floats using the specified adapter. Dispatches float arrays to the registered * buffer listener. */ -public class MixerReader implements Runnable +public class MixerReader { private final static Logger mLog = LoggerFactory.getLogger(MixerReader.class); - - private static final long BUFFER_PROCESSING_INTERVAL_MS = 50; //50 milliseconds, 20 reads per second private TargetDataLine mTargetDataLine; - private byte[] mBuffer; private int mBufferSize; private AtomicBoolean mRunning = new AtomicBoolean(); - private ScheduledFuture mScheduledFuture; - private int mBytesRead; private ISampleAdapter mSampleAdapter; private Listener mListener; private Listener mSourceEventListener; private AudioFormat mAudioFormat; private HeartbeatManager mHeartbeatManager; + private Thread mReaderThread; + /** + * Constructs an instance + * @param audioFormat for the target mixer + * @param targetDataLine to read samples from + * @param abstractSampleAdapter to convert samples to native buffers + * @param heartbeatManager to ping before each buffer read for downstream consumers + */ public MixerReader(AudioFormat audioFormat, TargetDataLine targetDataLine, ISampleAdapter abstractSampleAdapter, HeartbeatManager heartbeatManager) { @@ -64,10 +63,22 @@ public class MixerReader implements Runnable mSampleAdapter = abstractSampleAdapter; mHeartbeatManager = heartbeatManager; - /* Set buffer size to 1/10 second of samples */ - mBufferSize = (int)(mAudioFormat.getSampleRate() * 0.1) * mAudioFormat.getFrameSize(); + if(mAudioFormat.getSampleRate() <= 96000) + { + setBufferSampleSize(4096); + } + else + { + setBufferSampleSize(8192); + } } + /** + * Constructs an instance + * @param audioFormat for the target mixer + * @param targetDataLine to read samples from + * @param abstractSampleAdapter to convert samples to native buffers + */ public MixerReader(AudioFormat audioFormat, TargetDataLine targetDataLine, ISampleAdapter abstractSampleAdapter) { this(audioFormat, targetDataLine, abstractSampleAdapter, new HeartbeatManager()); @@ -82,54 +93,12 @@ public class MixerReader implements Runnable } /** - * Sets the size of buffers to use. This reader will read from target data line 20 times per second. - * @param bytesPerBuffer to size for each buffer read. + * Sets the number of samples per native buffer to produce. + * @param samples is the number of samples */ - public void setBufferSize(int bytesPerBuffer) + public void setBufferSampleSize(int samples) { - mBufferSize = bytesPerBuffer; - mBuffer = new byte[mBufferSize]; - } - - /** - * Opens the source mixer target data line - * - * @throws LineUnavailableException if the target data line is null or unavailable (from the OS). - */ - private void openTargetDataLine() throws LineUnavailableException - { - if(mTargetDataLine == null) - { - throw new LineUnavailableException("Source Mixer TargetDataLine is null"); - } - - mBuffer = new byte[mBufferSize]; - - mTargetDataLine.open(getAudioFormat()); - mLog.info("TDL Open:" + mTargetDataLine.isOpen() + " Active:" + mTargetDataLine.isActive() + " Running:" + mTargetDataLine.isRunning()); - mLog.info("Format:" + mTargetDataLine.getFormat().toString()); - mTargetDataLine.start(); - mLog.info("TDL Open:" + mTargetDataLine.isOpen() + " Active:" + mTargetDataLine.isActive() + " Running:" + mTargetDataLine.isRunning()); - - } - - /** - * Closes the mixer source target data line - */ - private void closeTargetDataLine() - { - if(mTargetDataLine != null && mTargetDataLine.isOpen()) - { - if(mTargetDataLine.isRunning()) - { - mTargetDataLine.stop(); - } - - if(mTargetDataLine.isOpen()) - { - mTargetDataLine.close(); - } - } + mBufferSize = samples * mAudioFormat.getFrameSize(); } /** @@ -139,9 +108,17 @@ public class MixerReader implements Runnable { if(mRunning.compareAndSet(false, true)) { + if(mTargetDataLine == null) + { + mRunning.set(false); + mLog.error("Attempt to start failed - target data line is null"); + return; + } + try { - openTargetDataLine(); + mTargetDataLine.open(getAudioFormat()); + mTargetDataLine.start(); } catch(LineUnavailableException e) { @@ -150,15 +127,9 @@ public class MixerReader implements Runnable return; } - //Cancel the scheduled thread if it wasn't cancelled previously ... this shouldn't happen - if(mScheduledFuture != null) - { - mScheduledFuture.cancel(true); - mScheduledFuture = null; - } - - mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(this, - 0, BUFFER_PROCESSING_INTERVAL_MS, TimeUnit.MILLISECONDS); + mReaderThread = new Thread(new DataLineReader()); + mReaderThread.setName("sdrtrunk mixer sample reader"); + mReaderThread.start(); } else { @@ -173,66 +144,31 @@ public class MixerReader implements Runnable { if(mRunning.compareAndSet(true, false)) { - if(mScheduledFuture != null) + if(mTargetDataLine != null && mTargetDataLine.isOpen()) { - mScheduledFuture.cancel(true); - mScheduledFuture = null; + if(mTargetDataLine.isRunning()) + { + mTargetDataLine.stop(); + } + + if(mTargetDataLine.isOpen()) + { + mTargetDataLine.close(); + } } - closeTargetDataLine(); - } - else - { - mLog.warn("Attempt to stop an already stopped BufferReader instance - this shouldn't happen"); - } - } - - /** - * Sample processing thread. This method runs each time the timer fires. - */ - @Override - public void run() - { - if(mRunning.get()) - { - if(mHeartbeatManager != null) - { - mHeartbeatManager.broadcast(); - } + mReaderThread.interrupt(); try { - mBytesRead = 0; - - //Blocking read - waits until the buffer fills - mBytesRead = mTargetDataLine.read(mBuffer, 0, mBuffer.length); - - if(mBytesRead == mBuffer.length) - { - //Sample adapter automatically sets initial listener count to one. - T reusableBuffer = mSampleAdapter.convert(mBuffer); - - if(reusableBuffer != null && mListener != null) - { - mListener.receive(reusableBuffer); - } - } - else if(mBytesRead > 0) - { - //Sample adapter automatically sets initial listener count to one. - T reusableBuffer = mSampleAdapter.convert(Arrays.copyOf(mBuffer, mBytesRead)); - - if(reusableBuffer != null && mListener != null) - { - mListener.receive(reusableBuffer); - } - } + mReaderThread.join(500); } - catch(Throwable t) + catch(InterruptedException ie) { - mLog.error("MixerSource - error while reading from the mixer target data line", t); - stop(); + //No-op ... we're shutting down } + + mReaderThread = null; } } @@ -296,9 +232,49 @@ public class MixerReader implements Runnable public void dispose() { - stop(); mHeartbeatManager = null; mListener = null; mSourceEventListener = null; } + + /** + * Runnable reader to read samples from the target data line, convert them to native buffers, and dispatch. + */ + public class DataLineReader implements Runnable + { + @Override + public void run() + { + while(mRunning.get()) + { + if(mHeartbeatManager != null) + { + mHeartbeatManager.broadcast(); + } + + byte[] buffer = new byte[mBufferSize]; + + int bytesRead = 0; + + try + { + while(bytesRead < buffer.length) + { + bytesRead += mTargetDataLine.read(buffer, bytesRead, buffer.length); + } + } + catch(ArrayIndexOutOfBoundsException aioobe) + { + //No-op ... this can happen during USB disconnect + } + + //We'll always read the correct number of bytes until the data line is closed or stopped, and we're + //shutting down. + if(bytesRead == buffer.length && mListener != null) + { + mListener.receive(mSampleAdapter.convert(buffer)); + } + } + } + } } diff --git a/src/main/java/io/github/dsheirer/source/recording/Recording.java b/src/main/java/io/github/dsheirer/source/recording/Recording.java deleted file mode 100644 index e1773c8a..00000000 --- a/src/main/java/io/github/dsheirer/source/recording/Recording.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.source.recording; - -import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.ISourceEventProcessor; -import io.github.dsheirer.source.SourceEvent; -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.channel.TunerChannel; -import io.github.dsheirer.source.tuner.channel.TunerChannelSource; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class Recording implements Comparable, ISourceEventProcessor -{ - private RecordingConfiguration mConfiguration; - - private List mTunerChannels = new ArrayList(); - - private long mCenterFrequency; - - private SettingsManager mSettingsManager; - - public Recording( SettingsManager settingsManager, - RecordingConfiguration configuration ) - { - mSettingsManager = settingsManager; - mConfiguration = configuration; - mCenterFrequency = mConfiguration.getCenterFrequency(); - } - - public void setAlias( String alias ) - { - mConfiguration.setAlias( alias ); -// mSettingsManager.save(); - } - - public void setRecordingFile( File file ) throws SourceException - { - if( hasChannels() ) - { - throw new SourceException( "Recording source - can't change " - + "recording file while channels are enabled against the " - + "current recording." ); - } - - mConfiguration.setFilePath( file.getAbsolutePath() ); -// mSettingsManager.save(); - } - - /** - * Indicates if this recording is currently playing/providing samples to - * tuner channel sources - */ - public boolean hasChannels() - { - return mTunerChannels.size() > 0; - } - - public TunerChannelSource getChannel( TunerChannel channel ) - { - // TODO Auto-generated method stub - return null; - } - - public RecordingConfiguration getRecordingConfiguration() - { - return mConfiguration; - } - - public String toString() - { - return mConfiguration.getAlias(); - } - - @Override - public void process(SourceEvent event ) throws SourceException - { - if( event.getEvent() == SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - long frequency = event.getValue().longValue(); - - mConfiguration.setCenterFrequency( frequency ); - -// mSettingsManager.save(); - - mCenterFrequency = frequency; - - for( TunerChannelSource channel: mTunerChannels ) - { - channel.process( event ); - } - } - } - - @Override - public int compareTo( Recording other ) - { - return getRecordingConfiguration().getAlias() - .compareTo( other.getRecordingConfiguration().getAlias() ); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Recording)) return false; - return compareTo((Recording) o) == 0; - } -} diff --git a/src/main/java/io/github/dsheirer/source/recording/RecordingConfiguration.java b/src/main/java/io/github/dsheirer/source/recording/RecordingConfiguration.java index 7dea1236..35ba0390 100644 --- a/src/main/java/io/github/dsheirer/source/recording/RecordingConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/recording/RecordingConfiguration.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.source.recording; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -7,6 +26,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.io.File; @JacksonXmlRootElement(localName = "recording_configuration") +@Deprecated //No longer used -- retained for backward compatibility with existing user configuration files. public class RecordingConfiguration { private File mRecording; @@ -24,24 +44,6 @@ public class RecordingConfiguration mFilePath = filePath; mAlias = alias; mCenterFrequency = centerFrequency; - - updateFile(); - } - - private void updateFile() - { - if(mFilePath != null) - { - try - { - mRecording = new File(mFilePath); - } - catch(Exception e) - { - throw new IllegalArgumentException("There was an error while " - + "accessing the recording source [" + mFilePath + "]", e); - } - } } @JsonIgnore @@ -59,8 +61,6 @@ public class RecordingConfiguration public void setFilePath(String filePath) { mFilePath = filePath; - - updateFile(); } @JacksonXmlProperty(isAttribute = true, localName = "alias") diff --git a/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java b/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java deleted file mode 100644 index 9df7d447..00000000 --- a/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.source.recording; - -import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.Source; -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.config.SourceConfigRecording; -import io.github.dsheirer.source.config.SourceConfiguration; -import io.github.dsheirer.source.tuner.channel.ChannelSpecification; -import io.github.dsheirer.source.tuner.channel.TunerChannel; -import io.github.dsheirer.source.tuner.channel.TunerChannelSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class RecordingSourceManager -{ - private final static Logger mLog = LoggerFactory.getLogger(RecordingSourceManager.class); - - private ArrayList mRecordings = new ArrayList(); - - private SettingsManager mSettingsManager; - - public RecordingSourceManager(SettingsManager settingsManager) - { - mSettingsManager = settingsManager; - loadRecordings(); - } - - /** - * Iterates current recordings to get a tuner channel source for the frequency - * specified in the channel config's source config object - */ - public Source getSource(SourceConfiguration config, ChannelSpecification channelSpecification) - throws SourceException - { - TunerChannelSource retVal = null; - - if(config instanceof SourceConfigRecording) - { - SourceConfigRecording configRecording = (SourceConfigRecording)config; - - TunerChannel tunerChannel = configRecording.getTunerChannel(); - - tunerChannel.setBandwidth(channelSpecification.getBandwidth()); - - Recording recording = getRecordingFromAlias( - configRecording.getRecordingAlias()); - - if(recording != null) - { - retVal = recording.getChannel(tunerChannel); - } - else - { - throw new SourceException("Recording with alias name [" + - configRecording.getRecordingAlias() + "] is not currently available"); - } - } - - return retVal; - } - - public Recording getRecordingFromAlias(String alias) - { - for(Recording recording : mRecordings) - { - if(recording.getRecordingConfiguration() - .getAlias().contentEquals(alias)) - { - return recording; - } - } - - return null; - } - - /** - * Get list of loaded IQ files - */ - public List getRecordings() - { - Collections.sort(mRecordings); - - return mRecordings; - } - - public void addRecording(Recording recording) - { - mRecordings.add(recording); - } - - public Recording addRecording(RecordingConfiguration config) - { - Recording recording = new Recording(mSettingsManager, config); - - mRecordings.add(recording); - - return recording; - } - - public void removeRecording(Recording recording) - { - /* Remove the config from the settings manager */ - mSettingsManager.removeRecordingConfiguration( - recording.getRecordingConfiguration()); - - mRecordings.remove(recording); - } - - private void loadRecordings() - { - List recordingConfigurations = - mSettingsManager.getRecordingConfigurations(); - - mLog.info("RecordingSourceManager - discovered [" + - recordingConfigurations.size() + "] recording configurations"); - - for(RecordingConfiguration config : recordingConfigurations) - { - Recording recording = new Recording(mSettingsManager, config); - - mRecordings.add(recording); - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/LoggingTunerErrorListener.java b/src/main/java/io/github/dsheirer/source/tuner/LoggingTunerErrorListener.java new file mode 100644 index 00000000..785b4a0f --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/LoggingTunerErrorListener.java @@ -0,0 +1,37 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tuner error listener that logs any received tuner error messages + */ +public class LoggingTunerErrorListener implements ITunerErrorListener +{ + private Logger mLog = LoggerFactory.getLogger(LoggingTunerErrorListener.class); + + @Override + public void setErrorMessage(String errorMessage) + { + mLog.error("Tuner Error: " + errorMessage); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/MixerTunerType.java b/src/main/java/io/github/dsheirer/source/tuner/MixerTunerType.java index 25a81176..7a2655f5 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/MixerTunerType.java +++ b/src/main/java/io/github/dsheirer/source/tuner/MixerTunerType.java @@ -1,20 +1,21 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner; import javax.sound.sampled.AudioFormat; @@ -53,28 +54,28 @@ public enum MixerTunerType true, //Signed false ) ); //Little Endian ) - private TunerType mTunerClass; + private TunerType mTunerType; private String mMixerName; private String mPartialDescription; private String mDisplayString; private AudioFormat mAudioFormat; - MixerTunerType( TunerType tunerClass, + MixerTunerType( TunerType tunerType, String mixerName, String partialDescription, String displayString, AudioFormat format ) { - mTunerClass = tunerClass; + mTunerType = tunerType; mMixerName = mixerName; mPartialDescription = partialDescription; mDisplayString = displayString; mAudioFormat = format; } - public TunerType getTunerClass() + public TunerType getTunerType() { - return mTunerClass; + return mTunerType; } public String getMixerName() diff --git a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java index d2f5cc92..6b27b69b 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java @@ -1,32 +1,29 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner; -import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.source.ChannelizerType; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.TunerEvent.Event; import io.github.dsheirer.source.tuner.manager.ChannelSourceManager; import io.github.dsheirer.source.tuner.manager.HeterodyneChannelSourceManager; @@ -46,31 +43,28 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene private ChannelSourceManager mChannelSourceManager; private TunerController mTunerController; private TunerFrequencyErrorMonitor mTunerFrequencyErrorMonitor; - private String mName; - private String mErrorMessage; + private ITunerErrorListener mTunerErrorListener; - public Tuner(String name, TunerController tunerController) + public Tuner(TunerController tunerController, ITunerErrorListener tunerErrorListener) { - mName = name; mTunerController = tunerController; + mTunerErrorListener = tunerErrorListener; //Register to receive frequency and sample rate change notifications mTunerController.addListener(this::process); - mTunerController.setTunerErrorListener(this); mTunerFrequencyErrorMonitor = new TunerFrequencyErrorMonitor(this); mTunerFrequencyErrorMonitor.start(); } /** * Abstract tuner class. - * @param name of the tuner * @param tunerController for the tuner - * @param userPreferences to discover preferred channelizer type + * @param tunerErrorListener to listen for tuner errors + * @param channelizerType for the channelizer */ - public Tuner(String name, TunerController tunerController, UserPreferences userPreferences) + public Tuner(TunerController tunerController, ITunerErrorListener tunerErrorListener, ChannelizerType channelizerType) { - this(name, tunerController); + this(tunerController, tunerErrorListener); - ChannelizerType channelizerType = userPreferences.getTunerPreference().getChannelizerType(); if(channelizerType == ChannelizerType.POLYPHASE) { setChannelSourceManager(new PolyphaseChannelSourceManager(mTunerController)); @@ -85,6 +79,58 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene } } + /** + * Perform startup operations + * @throws SourceException if there is an error that makes this tuner unusable + */ + public void start() throws SourceException + { + try + { + getTunerController().start(); + } + catch(SourceException se) + { + //Rethrow the source exception + throw se; + } + catch(Exception e) + { + //Wrap any other exceptions in a new source exception + mLog.error("Error starting " + getTunerClass() + " tuner", e); + throw new SourceException("Unable to start " + getTunerClass() + " tuner", e); + } + } + + /** + * Perform shutdown and disposal operations. + */ + public void stop() + { + if(getChannelSourceManager() != null) + { + getChannelSourceManager().stopAllChannels(); + } + + broadcast(new TunerEvent(this, Event.NOTIFICATION_SHUTTING_DOWN)); + getTunerController().stop(); + } + + /** + * Sets an unrecoverable error state for this tuner and propagates the error to an external listener + * @param errorMessage to set + */ + @Override + public void setErrorMessage(String errorMessage) + { + broadcast(new TunerEvent(this, Event.NOTIFICATION_ERROR_STATE)); + + if(mTunerErrorListener != null) + { + mTunerErrorListener.setErrorMessage(errorMessage); + } + } + /** * Sets the channel source manager * @param manager to use @@ -97,60 +143,32 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene mChannelSourceManager.addSourceEventListener(this::process); } - /** - * Sets this tuner to an error state with the errorMessage description. - * @param errorMessage describing the error state - */ - public void setErrorMessage(String errorMessage) - { - mLog.info("[" + getName() + "] tuner is now disabled for error [" + errorMessage + "]"); - mErrorMessage = errorMessage; - broadcast(new TunerEvent(this, Event.ERROR_STATE)); - getChannelSourceManager().setErrorMessage(errorMessage); - } - /** * Maximum number of bytes per second (MBps) produced by this tuner. * @return Bytes Per Second (Bps) */ public abstract int getMaximumUSBBitsPerSecond(); - /** - * Optional error message that describes an error state. - */ - public String getErrorMessage() - { - return mErrorMessage; - } - - /** - * Indicates if this tuner has an error. - */ - public boolean hasError() - { - return mErrorMessage != null; - } - @Override public void process(SourceEvent event) { switch(event.getEvent()) { case NOTIFICATION_CHANNEL_COUNT_CHANGE: - broadcast(new TunerEvent(Tuner.this, Event.CHANNEL_COUNT)); + broadcast(new TunerEvent(Tuner.this, Event.UPDATE_CHANNEL_COUNT)); break; case NOTIFICATION_FREQUENCY_CHANGE: - broadcast(new TunerEvent(Tuner.this, Event.FREQUENCY_UPDATED)); + broadcast(new TunerEvent(Tuner.this, Event.UPDATE_FREQUENCY)); break; case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE: - broadcast(new TunerEvent(Tuner.this, Event.FREQUENCY_ERROR_UPDATED)); + broadcast(new TunerEvent(Tuner.this, Event.UPDATE_FREQUENCY_ERROR)); break; case NOTIFICATION_SAMPLE_RATE_CHANGE: - broadcast(new TunerEvent(Tuner.this, Event.SAMPLE_RATE_UPDATED)); + broadcast(new TunerEvent(Tuner.this, Event.UPDATE_SAMPLE_RATE)); break; case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED: case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED: - broadcast(new TunerEvent(Tuner.this, Event.LOCK_STATE_CHANGE)); + broadcast(new TunerEvent(Tuner.this, Event.UPDATE_LOCK_STATE)); break; case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED: mTunerFrequencyErrorMonitor.receive(event); @@ -159,7 +177,7 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene //ignore break; default: - mLog.debug("Unrecognized Source Event: " + event.toString()); + mLog.debug("Unrecognized Source Event: " + event); break; } } @@ -185,23 +203,7 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene */ public String toString() { - return mName + (hasError() ? mErrorMessage : ""); - } - - /** - * Dispose and prepare for shutdown - */ - public void dispose() - { - getTunerController().dispose(); - } - - /** - * Sets the name for this tuner - */ - public void setName(String name) - { - mName = name; + return getPreferredName(); } /** @@ -220,17 +222,17 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene /** * @return - tuner type enum entry */ - public abstract TunerType getTunerType(); + public TunerType getTunerType() + { + return getTunerController().getTunerType(); + } /** - * Name of this tuner object + * Preferred name for this. This should be unique and will be used to find this tuner (e.g. channel preferred tuner) * * @return - string name of this tuner object */ - public String getName() - { - return mName; - } + public abstract String getPreferredName(); /** * Sample size in bits @@ -240,7 +242,7 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene /** * Registers the listener */ - public void addTunerChangeListener(Listener listener) + public void addTunerEventListener(Listener listener) { mTunerEventBroadcaster.addListener(listener); } @@ -248,7 +250,7 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene /** * Removes the registered listener */ - public void removeTunerChangeListener(Listener listener) + public void removeTunerEventListener(Listener listener) { mTunerEventBroadcaster.removeListener(listener); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java b/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java index 5c23baad..028754aa 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java @@ -1,288 +1,118 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner; -import javax.usb.UsbDeviceDescriptor; +import java.util.EnumSet; +/** + * Tuner Class enumeration + */ public enum TunerClass { - AIRSPY( TunerType.AIRSPY_R820T, "1D50", "60A1", "Airspy", "Airspy" ), - GENERIC_2832( TunerType.RTL2832_VARIOUS, "0BDA", "2832", "RTL2832", "SDR" ), - GENERIC_2838( TunerType.RTL2832_VARIOUS, "0BDA", "2838", "RTL2832", "SDR" ), - COMPRO_VIDEOMATE_U620F( TunerType.ELONICS_E4000, "185B", "0620", "Compro", "Videomate U620F" ), - COMPRO_VIDEOMATE_U650F( TunerType.ELONICS_E4000, "185B", "0650", "Compro", "Videomate U620F" ), - COMPRO_VIDEOMATE_U680F( TunerType.ELONICS_E4000, "185B", "0680", "Compro", "Videomate U620F" ), - DEXATEK_LOGILINK_VG002A( TunerType.FCI_FC2580, "1D19", "1101", "Dexatek", "Logilink VG0002A" ), - DEXATEK_DIGIVOX_MINI_II_REV3( TunerType.FCI_FC2580, "1D19", "1102", "Dexatek", "MSI Digivox Mini II v3.0" ), - DEXATEK_5217_DVBT( TunerType.FCI_FC2580, "1D19", "1103", "Dexatek", "5217 DVB-T" ), - ETTUS_USRP_B100( TunerType.ETTUS_VARIOUS, "2500", "0002", "Ettus Research", "USRP B100" ), - FUNCUBE_DONGLE_PRO( TunerType.FUNCUBE_DONGLE_PRO, "04D8", "FB56", "Hamlincrest", "Funcube Dongle Pro" ), - FUNCUBE_DONGLE_PRO_PLUS( TunerType.FUNCUBE_DONGLE_PRO_PLUS, "04D8", "FB31", "Hamlincrest", "Funcube Dongle Pro Plus" ), - GIGABYTE_GTU7300( TunerType.FITIPOWER_FC0012, "1B80", "D393", "Gigabyte", "GT-U7300" ), - GTEK_T803( TunerType.FITIPOWER_FC0012, "1F4D", "B803", "GTek", "T803" ), - HACKRF_ONE( TunerType.HACKRF, "1D50", "6089", "Great Scott Gadgets", "HackRF One" ), - HACKRF_JAWBREAKER( TunerType.HACKRF, "1D50", "604B", "Great Scott Gadgets", "HackRF Jawbreaker" ), - RAD1O( TunerType.HACKRF, "1D50", "CC15", "Munich hackerspace", "Rad1o" ), - LIFEVIEW_LV5T_DELUXE( TunerType.FITIPOWER_FC0012, "1F4D", "C803", "Liveview", "LV5T Deluxe" ), - MYGICA_TD312( TunerType.FITIPOWER_FC0012, "1F4D", "D286", "MyGica", "TD312" ), - PEAK_102569AGPK( TunerType.FITIPOWER_FC0012, "1B80", "D395", "Peak", "102569AGPK" ), - PROLECTRIX_DV107669( TunerType.FITIPOWER_FC0012, "1F4D", "D803", "Prolectrix", "DV107669" ), - SVEON_STV20( TunerType.FITIPOWER_FC0012, "1B80", "D39D", "Sveon", "STV20 DVB-T USB & FM" ), - TERRATEC_CINERGY_T_REV1( TunerType.FITIPOWER_FC0012, "0CCD", "00A9", "Terratec", "Cinergy T R1" ), - TERRATEC_CINERGY_T_REV3( TunerType.ELONICS_E4000, "0CCD", "00D3", "Terratec", "Cinergy T R3" ), - TERRATEC_NOXON_REV1_B3( TunerType.FITIPOWER_FC0013, "0CCD", "00B3", "Terratec", "NOXON R1 (B3)" ), - TERRATEC_NOXON_REV1_B4( TunerType.FITIPOWER_FC0013, "0CCD", "00B4", "Terratec", "NOXON R1 (B4)" ), - TERRATEC_NOXON_REV1_B7( TunerType.FITIPOWER_FC0013, "0CCD", "00B7", "Terratec", "NOXON R1 (B7)" ), - TERRATEC_NOXON_REV1_C6( TunerType.FITIPOWER_FC0013, "0CCD", "00C6", "Terratec", "NOXON R1 (C6)" ), - TERRATEC_NOXON_REV2( TunerType.ELONICS_E4000, "0CCD", "00E0", "Terratec", "NOXON R2" ), - TERRATEC_T_STICK_PLUS( TunerType.ELONICS_E4000, "0CCD", "00D7", "Terratec", "T Stick Plus" ), - TWINTECH_UT40( TunerType.FITIPOWER_FC0013, "1B80", "D3A4", "Twintech", "UT-40" ), - ZAAPA_ZTMINDVBZP( TunerType.FITIPOWER_FC0012, "1B80", "D398", "Zaapa", "ZT-MINDVBZP" ), - TEST_TUNER(TunerType.TEST, "0", "0", "ABC Tuners Inc.", "Model XYZ"), - RECORDING_TUNER(TunerType.RECORDING, "0", "0", "Recording Tuner", "Recording"), - UNKNOWN( TunerType.UNKNOWN, "0", "0", "Unknown Manufacturer", "Unknown Device" ); + AIRSPY("Airspy"), + FUNCUBE_DONGLE_PRO("Funcube Dongle Pro" ), + FUNCUBE_DONGLE_PRO_PLUS("Funcube Dongle Pro+" ), + HACKRF("HackRF" ), + RTL2832("RTL-2832"), + TEST_TUNER("Test"), + RECORDING_TUNER("Recording"), + UNKNOWN("Unknown" ); - private TunerType mTunerType; - private String mVendorID; - private String mDeviceID; - private String mVendorDescription; - private String mDeviceDescription; - - private TunerClass( TunerType tunerType, - String vendorID, - String deviceID, - String vendorDescription, - String deviceDescription ) + private String mDescription; + + /** + * Constructs an entry + * @param description of the entry + */ + TunerClass(String description) { - mTunerType = tunerType; - mVendorID = vendorID; - mDeviceID = deviceID; - mVendorDescription = vendorDescription; - mDeviceDescription = deviceDescription; + mDescription = description; } - + + @Override public String toString() { - return "USB" + - " Tuner:" + mTunerType.toString() + - " Vendor:" + mVendorDescription + - " Device:" + mDeviceDescription + - " Address:" + mVendorID + ":" + mDeviceID; + return mDescription; } - - public String getVendorDeviceLabel() - { - return mVendorDescription + " " + mDeviceDescription; - } - - public TunerType getTunerType() - { - return mTunerType; - } - - public static TunerClass valueOf( UsbDeviceDescriptor descriptor ) - { - return valueOf( descriptor.idVendor(), descriptor.idProduct() ); - } - - public static TunerClass valueOf( short vendor, short product ) - { - TunerClass retVal = TunerClass.UNKNOWN; - //Cast the short to integer so that we can switch on unsigned numbers - int vendorID = vendor & 0xFFFF; - int productID = product & 0xFFFF; - - switch( vendorID ) + public static final EnumSet SUPPORTED_USB_TUNERS = EnumSet.of(AIRSPY, HACKRF, RTL2832, + FUNCUBE_DONGLE_PRO, FUNCUBE_DONGLE_PRO_PLUS); + + /** + * Indicates if this tuner class entry is a supported USB tuner class. + */ + public boolean isSupportedUsbTuner() + { + return SUPPORTED_USB_TUNERS.contains(this); + } + + /** + * Lookup a USB tuner class from the USB vid and pid + * @param vendor id + * @param product id + * @return tuner class or UNKNOWN + */ + public static TunerClass lookup(short vendor, short product) + { + //Combine vid & pid to make an integer that we can switch on + int id = ((vendor & 0xFFFF) << 16) | (product & 0xFFFF); + + switch(id) { - case 1240: //04DA - if( productID == 64305 ) //FB31 - { - retVal = FUNCUBE_DONGLE_PRO_PLUS; - } - else if( productID == 64342 ) //FB56 - { - retVal = FUNCUBE_DONGLE_PRO; - } - break; - case 3034: //0BDA - if( productID == 10290 ) //2832 - { - retVal = GENERIC_2832; - } - else if( productID == 10296 ) //2838 - { - retVal = GENERIC_2838; - } - break; - case 6235: //185B - if( productID == 1568 ) //0620 - { - retVal = COMPRO_VIDEOMATE_U620F; - } - else if( productID == 1616 ) //0650 - { - retVal = COMPRO_VIDEOMATE_U650F; - } - else if( productID == 1664 ) //0680 - { - retVal = COMPRO_VIDEOMATE_U680F; - } - break; - case 7040: //1B80 - if( productID == 54163 ) //D393 - { - retVal = GIGABYTE_GTU7300; - } - else if( productID == 54165 ) //D395 - { - retVal = PEAK_102569AGPK; - } - else if( productID == 54168 ) //D398 - { - retVal = ZAAPA_ZTMINDVBZP; - } - else if( productID == 54173 ) //D39D - { - retVal = SVEON_STV20; - } - else if( productID == 54180 ) //D3A4 - { - retVal = TWINTECH_UT40; - } - break; - case 7449: //1D19 - if( productID == 4353 ) //1101 - { - retVal = DEXATEK_LOGILINK_VG002A; - } - else if( productID == 4354 ) //1102 - { - retVal = DEXATEK_DIGIVOX_MINI_II_REV3; - } - else if( productID == 4355 ) //1103 - { - retVal = DEXATEK_5217_DVBT; - } - break; - case 7504: //1D50 - if( productID == 24713 ) //6089 - { - retVal = HACKRF_ONE; - } - else if( productID == 24737 ) //60A1 - { - retVal = AIRSPY; - } - else if( productID == 52245 ) //CC15 - { - retVal = HACKRF_ONE; - } - else if( productID == 24651 ) //604B - { - retVal = HACKRF_JAWBREAKER; - } - break; - case 8013: //1F4D - if( productID == 47107 ) //B803 - { - retVal = GTEK_T803; - } - else if( productID == 51203 ) //C803 - { - retVal = LIFEVIEW_LV5T_DELUXE; - } - else if( productID == 53894 ) //D286 - { - retVal = MYGICA_TD312; - } - else if( productID == 55299 ) //D803 - { - retVal = PROLECTRIX_DV107669; - } - break; - case 3277: //0CCD - if( productID == 169 ) //00A9 - { - retVal = TERRATEC_CINERGY_T_REV1; - } - else if( productID == 179 ) //00B3 - { - retVal = TERRATEC_NOXON_REV1_B3; - } - else if( productID == 180 ) //00B4 - { - retVal = TERRATEC_NOXON_REV1_B4; - } - else if( productID == 181 ) //00B5 - { - retVal = TERRATEC_NOXON_REV1_B7; - } - else if( productID == 198 ) //00C6 - { - retVal = TERRATEC_NOXON_REV1_C6; - } - else if( productID == 211 ) //00D3 - { - retVal = TERRATEC_CINERGY_T_REV3; - } - else if( productID == 215 ) //00D7 - { - retVal = TERRATEC_T_STICK_PLUS; - } - else if( productID == 224) //00E0 - { - retVal = TERRATEC_NOXON_REV2; - } - break; - case 9472: //2500 - if( productID == 2 ) //0002 - { - retVal = ETTUS_USRP_B100; - } - break; - default: + case 0x04D8FB31: + return FUNCUBE_DONGLE_PRO_PLUS; + case 0x04D8FB56: + return FUNCUBE_DONGLE_PRO; + case 0x0BDA2832: //GENERIC RTL-2832 + case 0x0BDA2838: //GENERIC RTL-2832/2838 + case 0x185B0620: //COMPRO VIDEOMATE U620F + case 0x185B0650: //COMPRO VIDEOMATE U650F + case 0x185B0680: //COMPRO VIDEOMATE U680F + case 0x1B80D393: //GIGABYTE GTU7300 + case 0x1B80D395: //PEAK 102569AGPK + case 0x1B80D398: //ZAAPA ZTMINDVBZP + case 0x1B80D39D: //SVEON_STV20 + case 0x1B80D3A4: //TWINTECH UT40 + case 0x1D191101: //DEXATEK LIGILINK VG002A + case 0x1D191102: //DEXATEK DIGIVOX MINI II REV3 + case 0x1D191103: //DEXATEK 5217 DVBT + case 0x1F4DB803: //GTEK T803 + case 0x1F4DC803: //LIFEVIEW LV5T DELUXE + case 0x1F4DD286: //MIGICA TD312 + case 0x1F4DD803: //PROLECTRIX DV107669 + case 0x0CCD00A9: //TERRATEC CINERGY T REV1 + case 0x0CCD00B3: //TERRATEC NOXON REV1 B3 + case 0x0CCD00B4: //TERRATEC NOXON REV1 B4 + case 0x0CCD00B7: //TERRATEC NOXON REV1 B7 + case 0x0CCD00C6: //TERRATEC NOXON REV1 C6 + case 0x0CCD00D3: //TERRATEC CINERGY T REV3 + case 0x0CCD00D7: //TERRATEC T STICK PLUS + case 0x0CCD00E0: //TERRATEC NOXON REV2 + return RTL2832; + case 0x1D506089: //HACKRF ONE + case 0x1D50CC15: //HACKRF ONE + case 0x1D50604B: //HACKRF JAWBREAKER + return HACKRF; + case 0x1D5060A1: + return AIRSPY; } - return retVal; - } - - public String getVendorID() - { - return mVendorID; - } - - public String getDeviceID() - { - return mDeviceID; - } - - public String getVendorDescription() - { - return mVendorDescription; - } - - public String getDeviceDescription() - { - return mDeviceDescription; + return TunerClass.UNKNOWN; } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java index 12a29117..7698efee 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java @@ -22,6 +22,7 @@ import io.github.dsheirer.buffer.INativeBuffer; import io.github.dsheirer.buffer.INativeBufferProvider; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.RecorderFactory; +import io.github.dsheirer.record.wave.IRecordingStatusListener; import io.github.dsheirer.record.wave.NativeBufferWaveRecorder; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; @@ -34,9 +35,11 @@ import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.frequency.FrequencyController; import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable; +import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.text.DecimalFormat; import java.util.SortedSet; public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener, @@ -51,31 +54,52 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, private int mMeasuredFrequencyError; private NativeBufferWaveRecorder mRecorder; private ITunerErrorListener mTunerErrorListener; + public FrequencyErrorCorrectionManager mFrequencyErrorCorrectionManager; + private DecimalFormat mFrequencyErrorPPMFormat = new DecimalFormat("0.0"); /** * Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels * that are being fed samples from the tuner. - * - * @param minimumFrequency minimum uncorrected tunable frequency - * @param maximumFrequency maximum uncorrected tunable frequency - * @param middleUnusableHalfBandwidth is the +/- value center DC spike to avoid for channels - * @param usableBandwidth percentage of usable bandwidth relative to space at the extreme ends of the spectrum - * @throws SourceException - for any issues related to constructing the - * class, tuning a frequency, or setting the bandwidth + * @param tunerErrorListener to monitor errors produced from this tuner controller */ - public TunerController(long minimumFrequency, long maximumFrequency, int middleUnusableHalfBandwidth, double usableBandwidth) + public TunerController(ITunerErrorListener tunerErrorListener) { - mFrequencyController = new FrequencyController(this, minimumFrequency, maximumFrequency, 0.0d); - mMiddleUnusableHalfBandwidth = middleUnusableHalfBandwidth; - mUsableBandwidthPercentage = usableBandwidth; + mTunerErrorListener = tunerErrorListener; + mFrequencyController = new FrequencyController(this); mSourceEventListener = new SourceEventListenerToProcessorAdapter(this); + mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this); } /** - * Dispose of this tuner controller and prepare for shutdown. + * Frequency correction manager for this tuner controller. */ - public abstract void dispose(); + public FrequencyErrorCorrectionManager getFrequencyErrorCorrectionManager() + { + return mFrequencyErrorCorrectionManager; + } + /** + * Perform startup operations + * @throws SourceException if there is an error that makes this tuner controller unusable + */ + public abstract void start() throws SourceException; + + /** + * Perform shutdown and disposal operations. + * + * Note: implementation should notify any native buffer listeners that we're shutting down. + */ + public abstract void stop(); + + /** + * Tuner type for this controller + */ + public abstract TunerType getTunerType(); + + /** + * Sets an unrecoverable error for this tuner controller that will be handled by the listener + * @param errorMessage to set + */ @Override public void setErrorMessage(String errorMessage) { @@ -85,15 +109,6 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, } } - /** - * Sets the listener for tuner error messages that originate outside of the tuner. This is normally the enclosing - * tuner so that it can receive error signals from the USB processor(s).. - */ - public void setTunerErrorListener(ITunerErrorListener tunerErrorListener) - { - mTunerErrorListener = tunerErrorListener; - } - /** * Number of samples contained in each complex buffer provided by this tuner. * @@ -121,7 +136,13 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, /** * Applies the settings in the tuner configuration */ - public abstract void apply(TunerConfiguration config) throws SourceException; + public void apply(TunerConfiguration config) throws SourceException + { + setFrequency(config.getFrequency()); + setFrequencyCorrection(config.getFrequencyCorrection()); + getFrequencyErrorCorrectionManager().setEnabled(config.getAutoPPMCorrectionEnabled()); + + } /** * Responds to requests to set the frequency @@ -230,16 +251,42 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, mFrequencyController.setFrequencyCorrection(correction); } - public long getMinFrequency() + /** + * Minimum tunable frequency + * @return minimum in Hertz + */ + public long getMinimumFrequency() { return mFrequencyController.getMinimumFrequency(); } - public long getMaxFrequency() + /** + * Sets the minimum tunable frequency + * @param minimum frequency in Hertz + */ + public void setMinimumFrequency(long minimum) + { + mFrequencyController.setMinimumFrequency(minimum); + } + + /** + * Maximum tunable frequency + * @return maximum in Hertz + */ + public long getMaximumFrequency() { return mFrequencyController.getMaximumFrequency(); } + /** + * Sets the maximum tunable frequency + * @param maximum in Hertz + */ + public void setMaximumFrequency(long maximum) + { + mFrequencyController.setMaximumFrequency(maximum); + } + public long getMinTunedFrequency() throws SourceException { return mFrequencyController.getFrequency() - (getUsableBandwidth() / 2); @@ -251,14 +298,23 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, } /** - * Total bandwidth of the middle unusable bandwidth region. This value is used to avoid a central DC spike - * present in some tuners. + * Half of the total bandwidth of the middle unusable bandwidth region. This value is used to avoid a central DC + * spike present in some tuners. */ public int getMiddleUnusableHalfBandwidth() { return mMiddleUnusableHalfBandwidth; } + /** + * Sets the middle unusable half bandwidth value + * @param halfBandwidth in Hertz + */ + public void setMiddleUnusableHalfBandwidth(int halfBandwidth) + { + mMiddleUnusableHalfBandwidth = halfBandwidth; + } + /** * Indicates if this tuner controller has a middle unusable bandwidth region. */ @@ -302,6 +358,25 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, return mMeasuredFrequencyError; } + /** + * Status of measured frequency and PPM error + */ + public String getMeasuredErrorStatus() + { + if(hasMeasuredFrequencyError()) + { + StringBuilder sb = new StringBuilder(); + sb.append("Measured Error: "); + sb.append(getMeasuredFrequencyError()); + sb.append("Hz ("); + sb.append(mFrequencyErrorPPMFormat.format(getPPMFrequencyError())); + sb.append("ppm)"); + return sb.toString(); + } + + return ""; + } + /** * Indicates if this tuner controller has a non-zero measured frequency error value. */ @@ -481,12 +556,14 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, /** * Records the complex I/Q buffers produced by the tuner * @param userPreferences to obtain a baseband recorder + * @param statusListener to receive updates on recording file name and size + * @param prefix for the recording file name (ie tuner class name) */ - public void startRecorder(UserPreferences userPreferences) + public void startRecorder(UserPreferences userPreferences, IRecordingStatusListener statusListener, String prefix) { if(!isRecording()) { - mRecorder = RecorderFactory.getTunerRecorder("TUNER_" + getFrequency(), userPreferences); + mRecorder = RecorderFactory.getTunerRecorder(prefix + "_" + getFrequency(), userPreferences, statusListener); mRecorder.setSampleRate((float)getSampleRate()); mRecorder.start(); addBufferListener(mRecorder); diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java deleted file mode 100644 index 3ec732e4..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ - -package io.github.dsheirer.source.tuner; - -import com.jidesoft.swing.JideSplitPane; -import io.github.dsheirer.gui.control.JFrequencyControl; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.gui.editor.EmptyEditor; -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationFactory; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JToggleButton; -import javax.swing.RowFilter; -import javax.swing.ScrollPaneConstants; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.TableRowSorter; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class TunerEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger(TunerEditor.class); - - private TunerConfigurationModel mTunerConfigurationModel; - private JTable mTunerConfigurationTable; - private TableRowSorter mRowSorter; - private JFrequencyControl mFrequencyControl = new JFrequencyControl(); - private JToggleButton mRecordButton; - private JLabel mSelectedTunerType = new JLabel("No Tuner Selected"); - private JButton mNewConfigurationButton = new JButton("New"); - private JButton mDeleteConfigurationButton = new JButton("Delete"); - private JButton mAssignConfigurationButton = new JButton("Assign"); - private JScrollPane mEditorScroller; - private Editor mEditor = new EmptyEditor<>("a tuner"); - private JideSplitPane mEditorSplitPane = new JideSplitPane(); - private UserPreferences mRecorderManager; - - public TunerEditor(TunerConfigurationModel tunerConfigurationModel, UserPreferences userPreferences) - { - mTunerConfigurationModel = tunerConfigurationModel; - mRecorderManager = userPreferences; - init(); - } - - /** - * Updates the lock state of the tuner so that the frequency control and sample rate controls - * can be adjusted accordingly. - * - * @param locked true if the tuner is locked and the controls should be locked. - */ - public void setTunerLockState(boolean locked) - { - mFrequencyControl.setEnabled(!locked); - - if(mEditor instanceof TunerConfigurationEditor) - { - ((TunerConfigurationEditor)mEditor).setTunerLockState(locked); - } - } - - public void init() - { - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - - JPanel listPanel = new JPanel(); - listPanel.setLayout(new MigLayout("fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[][][][grow," + - "fill][]")); - - listPanel.add(mSelectedTunerType, "span"); - - mFrequencyControl.setEnabled(false); - listPanel.add(mFrequencyControl, "span 2"); - - mRecordButton = new JToggleButton("Record"); - mRecordButton.setEnabled(false); - mRecordButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - if(mRecordButton.isSelected()) - { - if(hasItem()) - { - getItem().getTunerController().startRecorder(mRecorderManager); - } - } - else - { - if(hasItem()) - { - getItem().getTunerController().stopRecorder(); - } - } - } - }); - listPanel.add(mRecordButton, "wrap"); - - mRowSorter = new TableRowSorter<>(mTunerConfigurationModel); - mTunerConfigurationTable = new JTable(mTunerConfigurationModel); - mTunerConfigurationTable.setRowSorter(mRowSorter); - mTunerConfigurationTable.setEnabled(false); - mTunerConfigurationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() - { - @Override - public void valueChanged(ListSelectionEvent event) - { - if(!event.getValueIsAdjusting()) - { - int row = mTunerConfigurationTable.getSelectedRow(); - - if(row >= 0) - { - int modelRow = mTunerConfigurationTable.convertRowIndexToModel(row); - - if(modelRow >= 0) - { - setTunerConfiguration(mTunerConfigurationModel.getTunerConfiguration(modelRow)); - } - } - } - } - }); - - JScrollPane tableScroller = new JScrollPane(mTunerConfigurationTable); - tableScroller.setHorizontalScrollBarPolicy( - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - listPanel.add(new JLabel("Tuner Configurations"), "span"); - listPanel.add(tableScroller, "span"); - - mNewConfigurationButton.setEnabled(false); - mNewConfigurationButton.setToolTipText("Create a new configuration for the currently selected tuner"); - mNewConfigurationButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - Tuner tuner = getItem(); - - String name = mTunerConfigurationModel - .getDistinctName(tuner.getTunerType(), tuner.getUniqueID()); - - TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration(tuner.getTunerType(), - tuner.getUniqueID(), name); - - mTunerConfigurationModel.addTunerConfiguration(config); - } - }); - listPanel.add(mNewConfigurationButton); - - mDeleteConfigurationButton.setEnabled(false); - mDeleteConfigurationButton.setToolTipText("Deletes the currently selected tuner"); - mDeleteConfigurationButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - int choice = JOptionPane.showConfirmDialog(TunerEditor.this, - "Do you want to delete this tuner configuration?", - "Delete Tuner Configuration?", JOptionPane.YES_NO_OPTION); - - if(choice == JOptionPane.YES_OPTION) - { - TunerConfiguration selected = getSelectedTunerConfiguration(); - - mTunerConfigurationModel.removeTunerConfiguration(selected); - mTunerConfigurationTable.setRowSelectionInterval(0, 0); - } - } - }); - listPanel.add(mDeleteConfigurationButton); - - mAssignConfigurationButton.setEnabled(false); - mAssignConfigurationButton.setToolTipText("Assigns to the currently selected tuner configuration to the selected tuner"); - mAssignConfigurationButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - TunerConfiguration selected = getSelectedTunerConfiguration(); - - mTunerConfigurationModel.assignTunerConfiguration(selected); - setTunerConfiguration(getSelectedTunerConfiguration()); - } - }); - listPanel.add(mAssignConfigurationButton); - - //Set preferred size on both scrollers to same width, so they split the space - listPanel.setPreferredSize(new Dimension(100, 80)); - JScrollPane listScroller = new JScrollPane(listPanel); - listScroller.setPreferredSize(new Dimension(100, 80)); - - mEditorScroller = new JScrollPane(mEditor); - mEditorScroller.setPreferredSize(new Dimension(100, 80)); - - mEditorSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT); - mEditorSplitPane.add(listScroller); - mEditorSplitPane.add(mEditorScroller); - - add(mEditorSplitPane); - } - - private TunerConfiguration getSelectedTunerConfiguration() - { - int tableRow = mTunerConfigurationTable.getSelectedRow(); - - if(tableRow >= 0) - { - int modelRow = mTunerConfigurationTable.convertRowIndexToModel(tableRow); - - if(modelRow >= 0) - { - return mTunerConfigurationModel.getTunerConfiguration(modelRow); - } - } - - return null; - } - - @Override - public void save() - { - //Unused - } - - @Override - public void setItem(Tuner tuner) - { - //Cleanup previously selected tuner - if(hasItem()) - { - Tuner previous = getItem(); - previous.getTunerController().removeListener(mFrequencyControl); - mFrequencyControl.removeListener(previous.getTunerController()); - } - - super.setItem(tuner); - - if(hasItem()) - { - mSelectedTunerType.setText(tuner.getName()); - mTunerConfigurationTable.setEnabled(true); - mNewConfigurationButton.setEnabled(true); - - //Change to an editor for this config's tuner type - mEditor = TunerConfigurationFactory.getEditor(getItem(), mTunerConfigurationModel); - - mRowSorter.setRowFilter(new ConfigurationRowFilter(tuner.getTunerType(), - tuner.getUniqueID())); - - TunerConfiguration assigned = mTunerConfigurationModel - .getTunerConfiguration(tuner.getTunerType(), tuner.getUniqueID()); - - int modelIndex = mTunerConfigurationModel.getRowIndex(assigned); - - if(modelIndex >= 0) - { - int viewIndex = mTunerConfigurationTable - .convertRowIndexToView(modelIndex); - - if(viewIndex >= 0) - { - mTunerConfigurationTable - .setRowSelectionInterval(viewIndex, viewIndex); - } - } - - //Link frequency control to the tuner and set enabled state - mFrequencyControl.addListener(tuner.getTunerController()); - mFrequencyControl.setEnabled(!tuner.getTunerController().isLocked()); - - //Link tuner to frequency control - tuner.getTunerController().addListener(mFrequencyControl); - - //Set the displayed frequency without adjusting the tuner's frequency - mFrequencyControl.setFrequency(tuner.getTunerController().getFrequency(), false); - - mRecordButton.setEnabled(true); - mRecordButton.setSelected(getItem().getTunerController().isRecording()); - } - else - { - mSelectedTunerType.setText("No Tuner Selected"); - mFrequencyControl.setEnabled(false); - mTunerConfigurationTable.setEnabled(false); - mNewConfigurationButton.setEnabled(false); - mRowSorter.setRowFilter(null); - mEditor = new EmptyEditor(); - mRecordButton.setEnabled(false); - mRecordButton.setSelected(false); - } - - //Swap out the editor - int split = mEditorSplitPane.getDividerLocation(0); - mEditorSplitPane.remove(mEditorScroller); - mEditorScroller = new JScrollPane(mEditor); - mEditorSplitPane.add(mEditorScroller); - mEditorSplitPane.setDividerLocation(0, split); - } - - private void setTunerConfiguration(TunerConfiguration config) - { - if(config != null) - { - if(!config.isAssigned()) - { - if(!mAssignConfigurationButton.isEnabled()) - { - mAssignConfigurationButton.setEnabled(true); - } - if(!mDeleteConfigurationButton.isEnabled()) - { - mDeleteConfigurationButton.setEnabled(true); - } - } - else - { - if(mAssignConfigurationButton.isEnabled()) - { - mAssignConfigurationButton.setEnabled(false); - } - - if(mDeleteConfigurationButton.isEnabled()) - { - mDeleteConfigurationButton.setEnabled(false); - } - } - } - else - { - if(mDeleteConfigurationButton.isEnabled()) - { - mDeleteConfigurationButton.setEnabled(false); - } - - if(mAssignConfigurationButton.isEnabled()) - { - mAssignConfigurationButton.setEnabled(false); - } - } - - mEditor.setItem(config); - } - - public class ConfigurationRowFilter extends RowFilter - { - private TunerType mTunerType; - private String mUniqueID; - - public ConfigurationRowFilter(TunerType type, String uniqueID) - { - mTunerType = type; - mUniqueID = uniqueID; - } - - @Override - public boolean include(javax.swing.RowFilter.Entry entry) - { - TunerConfiguration config = entry.getModel() - .getTunerConfiguration(entry.getIdentifier()); - - return config != null && - config.getTunerType() == mTunerType && - config.getUniqueID() != null && - config.getUniqueID().contentEquals(mUniqueID); - } - } - -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java b/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java index d4a4afe0..887ec2de 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java @@ -1,43 +1,62 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner; +/** + * Tuner generated events + */ public class TunerEvent { private Tuner mTuner; private Event mEvent; + /** + * Constructs an instance + * @param tuner that is generating the event + * @param event from the tuner + */ public TunerEvent(Tuner tuner, Event event) { mTuner = tuner; mEvent = event; } + /** + * Tuner source for the event + */ public Tuner getTuner() { return mTuner; } + /** + * Indicates if the tuner for this event is non-null + */ + public boolean hasTuner() + { + return mTuner != null; + } + + /** + * Event type + */ public Event getEvent() { return mEvent; @@ -45,15 +64,17 @@ public class TunerEvent public enum Event { - CHANNEL_COUNT, - ERROR_STATE, - FREQUENCY_UPDATED, - FREQUENCY_ERROR_UPDATED, - LOCK_STATE_CHANGE, - MEASURED_FREQUENCY_ERROR_UPDATED, - SAMPLE_RATE_UPDATED, + UPDATE_CHANNEL_COUNT, + UPDATE_FREQUENCY, + UPDATE_FREQUENCY_ERROR, + UPDATE_LOCK_STATE, + UPDATE_MEASURED_FREQUENCY_ERROR, + UPDATE_SAMPLE_RATE, - CLEAR_MAIN_SPECTRAL_DISPLAY, + NOTIFICATION_ERROR_STATE, + NOTIFICATION_SHUTTING_DOWN, + + REQUEST_CLEAR_MAIN_SPECTRAL_DISPLAY, REQUEST_MAIN_SPECTRAL_DISPLAY, REQUEST_NEW_SPECTRAL_DISPLAY; } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java b/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java new file mode 100644 index 00000000..865ea53b --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java @@ -0,0 +1,107 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner; + +import io.github.dsheirer.preference.source.ChannelizerType; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.mixer.MixerManager; +import io.github.dsheirer.source.tuner.airspy.AirspyTuner; +import io.github.dsheirer.source.tuner.airspy.AirspyTunerController; +import io.github.dsheirer.source.tuner.fcd.FCDTuner; +import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController; +import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerController; +import io.github.dsheirer.source.tuner.hackrf.HackRFTuner; +import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController; +import io.github.dsheirer.source.tuner.rtl.EmbeddedTuner; +import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; +import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TEmbeddedTuner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sound.sampled.TargetDataLine; + +/** + * Factory for creating tuners and tuner configurations + */ +public class TunerFactory +{ + private static final Logger mLog = LoggerFactory.getLogger(TunerFactory.class); + + /** + * Create a USB tuner + * @param tunerClass to instantiate + * @param port usb + * @param bus usb + * @param tunerErrorListener to listen for errors from the tuner + * @param channelizerType for the tuner + * @return instantiated tuner + * @throws SourceException if the tuner class is unrecognized + */ + public static Tuner getUsbTuner(TunerClass tunerClass, int port, int bus, ITunerErrorListener tunerErrorListener, + ChannelizerType channelizerType) throws SourceException + { + switch(tunerClass) + { + case AIRSPY: + return new AirspyTuner(new AirspyTunerController(bus, port, tunerErrorListener), tunerErrorListener, channelizerType); + case FUNCUBE_DONGLE_PRO: + TargetDataLine tdl1 = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO); + if(tdl1 != null) + { + FCD1TunerController controller = new FCD1TunerController(tdl1, bus, port, tunerErrorListener); + return new FCDTuner(controller, tunerErrorListener); + } + throw new SourceException("Unable to find matching tuner sound card mixer"); + case FUNCUBE_DONGLE_PRO_PLUS: + TargetDataLine tdl2 = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS); + if(tdl2 != null) + { + FCD2TunerController controller = new FCD2TunerController(tdl2, bus, port, tunerErrorListener); + return new FCDTuner(controller, tunerErrorListener); + } + throw new SourceException("Unable to find matching tuner sound card mixer"); + case HACKRF: + return new HackRFTuner(new HackRFTunerController(bus, port, tunerErrorListener), tunerErrorListener, channelizerType); + case RTL2832: + return new RTL2832Tuner(new RTL2832TunerController(bus, port, tunerErrorListener), tunerErrorListener, channelizerType); + default: + throw new SourceException("Unrecognized tuner class [" + tunerClass + "]"); + } + } + + /** + * Creates an RTL-2832 embedded tuner instance. + * @param tunerType to create + * @param adapter exposing the RTL-2832 tuner controller functions + * @return embedded tuner + */ + public static EmbeddedTuner getRtlEmbeddedTuner(TunerType tunerType, + RTL2832TunerController.ControllerAdapter adapter) throws SourceException + { + return switch(tunerType) + { + case ELONICS_E4000 -> new E4KEmbeddedTuner(adapter); + case RAFAELMICRO_R820T -> new R820TEmbeddedTuner(adapter); + default -> throw new SourceException("Unsupported/Unrecognized Tuner Type"); + }; + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerFrequencyErrorMonitor.java b/src/main/java/io/github/dsheirer/source/tuner/TunerFrequencyErrorMonitor.java index d27c48cd..cf284131 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerFrequencyErrorMonitor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerFrequencyErrorMonitor.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner; import io.github.dsheirer.sample.Listener; @@ -94,7 +97,7 @@ public class TunerFrequencyErrorMonitor implements Listener private void broadcast(int averageError) { mTuner.getTunerController().setMeasuredFrequencyError(averageError); - mTuner.broadcast(new TunerEvent(mTuner, TunerEvent.Event.MEASURED_FREQUENCY_ERROR_UPDATED)); + mTuner.broadcast(new TunerEvent(mTuner, TunerEvent.Event.UPDATE_MEASURED_FREQUENCY_ERROR)); } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java b/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java deleted file mode 100644 index 11ba8076..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java +++ /dev/null @@ -1,562 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.source.tuner; - -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.mixer.MixerManager; -import io.github.dsheirer.source.tuner.airspy.AirspyTuner; -import io.github.dsheirer.source.tuner.airspy.AirspyTunerController; -import io.github.dsheirer.source.tuner.fcd.FCDTuner; -import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController; -import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerController; -import io.github.dsheirer.source.tuner.hackrf.HackRFTuner; -import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController; -import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; -import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController; -import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController; -import io.github.dsheirer.source.tuner.usb.USBMasterProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceList; -import org.usb4java.LibUsb; - -import javax.sound.sampled.TargetDataLine; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -public class TunerManager -{ - private final static Logger mLog = LoggerFactory.getLogger(TunerManager.class); - private static final int MAXIMUM_USB_2_DATA_RATE = 480000000; - - private TunerModel mTunerModel; - private UserPreferences mUserPreferences; - private Map> mUSBBusTunerMap = new TreeMap<>(); - - /** - * Application-wide LibUSB timeout processor for transfer buffers. All classes that need to use USB transfer - * buffers can register with this processor and the processor will auto-start and auto-stop while USB transfer - * processors are registered. - */ - public static final USBMasterProcessor LIBUSB_TRANSFER_PROCESSOR; - - static - { - LIBUSB_TRANSFER_PROCESSOR = new USBMasterProcessor(); - } - - public TunerManager(TunerModel tunerModel, UserPreferences userPreferences) - { - mTunerModel = tunerModel; - mUserPreferences = userPreferences; - - initTuners(); - validateUSBBusTransferRates(); - } - - /** - * Performs cleanup of USB related issues - */ - public void dispose() - { - mLog.info("Shutting down LibUsb timeout processor ..."); - - LIBUSB_TRANSFER_PROCESSOR.shutdown(); - - mLog.info("Shutting down LibUsb ..."); - try - { - LibUsb.exit(null); - } - catch(Exception e) - { - mLog.error("Error shutting down LibUsb", e); - } - } - - /** - * Loads all USB tuners and USB/Mixer tuner devices - */ - private void initTuners() - { - DeviceList deviceList = new DeviceList(); - - int result = LibUsb.init(null); - - if(result != LibUsb.SUCCESS) - { - mLog.error("unable to initialize libusb [" + - LibUsb.errorName(result) + "]"); - } - else - { - mLog.info("LibUSB API Version: " + LibUsb.getApiVersion()); - mLog.info("LibUSB Version: " + LibUsb.getVersion()); - - result = LibUsb.getDeviceList(null, deviceList); - - if(result < 0) - { - mLog.error("unable to get device list from libusb [" + result + " / " + - LibUsb.errorName(result) + "]"); - } - else - { - mLog.info("discovered [" + result + "] attached USB devices"); - } - } - - for(Device device : deviceList) - { - DeviceDescriptor descriptor = new DeviceDescriptor(); - - result = LibUsb.getDeviceDescriptor(device, descriptor); - - if(result != LibUsb.SUCCESS) - { - mLog.error("unable to read device descriptor [" + - LibUsb.errorName(result) + "]"); - } - else - { - TunerInitStatus status = initTuner(device, descriptor); - - StringBuilder sb = new StringBuilder(); - - int bus = LibUsb.getBusNumber(device); - sb.append("USB Bus [").append(bus).append("]"); - sb.append(" Device ["); - sb.append(String.format("%04X", descriptor.idVendor())); - sb.append(":"); - sb.append(String.format("%04X", descriptor.idProduct())); - sb.append("]"); - - if(status.isLoaded()) - { - Tuner tuner = status.getTuner(); - - try - { - mTunerModel.addTuner(tuner); - sb.append(" LOADED: "); - sb.append(tuner.toString()); - sb.append(" Max Rate:").append(tuner.getMaximumUSBBitsPerSecond()).append(" bps"); - updateUSBBusTunerMap(bus, tuner); - } - catch(Exception e) - { - sb.append(" NOT LOADED: "); - sb.append(status.getInfo()); - sb.append(" Error:").append(e.getMessage()); - } - } - else - { - sb.append(" ").append(getDeviceClass(descriptor.bDeviceClass())); - } - - mLog.info(sb.toString()); - } - } - - LibUsb.freeDeviceList(deviceList, true); - } - - private static String getDeviceClass(byte deviceClass) - { - switch(deviceClass) - { - case 0: - return "Unknown Device - Class 0"; - case 2: - return "Communications Device"; - case 3: - return "HID Device"; - case 5: - return "Physical Device"; - case 6: - return "Still Imaging Device"; - case 7: - return "Printer Device"; - case 8: - return "Mass Storage Device"; - case 9: - return "Hub Device"; - case 0xA: - return "Communications Device"; - case 0xB: - return "Smart Card Device"; - case 0xD: - return "Content Security Device"; - case 0xE: - return "Video Device"; - case 0xF: - return "Personal Healthcare Device"; - case 0x10: - return "Audio/Video Device"; - case (byte)0xDC: - return "Diagnostic Device"; - case (byte)0xE0: - return "Wireless Controller Device"; - case (byte)0xEF: - return "Miscellaneous Device"; - default: - return "Unknown Device - Class " + deviceClass; - } - } - - private void updateUSBBusTunerMap(int bus, Tuner tuner) - { - if(mUSBBusTunerMap.containsKey(bus)) - { - mUSBBusTunerMap.get(bus).add(tuner); - } - else - { - List tuners = new ArrayList<>(); - tuners.add(tuner); - mUSBBusTunerMap.put(bus, tuners); - } - } - - private void validateUSBBusTransferRates() - { - mLog.info("-------------------------------------------------------------"); - mLog.info("USB Bus - Potential Maximum Data Rates"); - - boolean warning = false; - - List buses = new ArrayList(mUSBBusTunerMap.keySet()); - Collections.sort(buses); - for(Integer bus: buses) - { - int totalBPS = 0; - - for(Tuner tuner: mUSBBusTunerMap.get(bus)) - { - totalBPS += tuner.getMaximumUSBBitsPerSecond(); - } - - if(totalBPS > (MAXIMUM_USB_2_DATA_RATE * .7)) - { - warning = true; - } - - mLog.info("USB Bus [" + bus + "] Rate [" + totalBPS + "] bits per second" + (warning ? " *** WARNING ***" : "")); - } - - if(warning) - { - StringBuilder sb = new StringBuilder(); - sb.append( - "\n\t\t******************************************************************************************************\n" + - "\t\t* WARNING: the combined data rates of tuner(s) on USB bus(es) above can potentially saturate or exceed\n" + - "\t\t* the maximum USB 2.0 data rate of 480 Mbps. This can cause data loss and severely impact decoding\n" + - "\t\t* performance resulting in missed calls and stuttering audio. Recommend moving tuner(s) to other\n" + - "\t\t* USB ports/buses to better balance data rates across available USB buses.\n" + - "\t\t******************************************************************************************************"); - mLog.warn(sb.toString()); - } - } - - private TunerInitStatus initTuner(Device device, DeviceDescriptor descriptor) - { - if(device != null && descriptor != null) - { - TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(), descriptor.idProduct()); - - switch(tunerClass) - { - case AIRSPY: - return initAirspyTuner(device, descriptor); - case ETTUS_USRP_B100: - return initEttusB100Tuner(device, descriptor); - case FUNCUBE_DONGLE_PRO: - return initFuncubeProTuner(device, descriptor); - case FUNCUBE_DONGLE_PRO_PLUS: - return initFuncubeProPlusTuner(device, descriptor); - case HACKRF_ONE: - case HACKRF_JAWBREAKER: - case RAD1O: - return initHackRFTuner(device, descriptor); - case COMPRO_VIDEOMATE_U620F: - case COMPRO_VIDEOMATE_U650F: - case COMPRO_VIDEOMATE_U680F: - case GENERIC_2832: - case GENERIC_2838: - case DEXATEK_5217_DVBT: - case DEXATEK_DIGIVOX_MINI_II_REV3: - case DEXATEK_LOGILINK_VG002A: - case GIGABYTE_GTU7300: - case GTEK_T803: - case LIFEVIEW_LV5T_DELUXE: - case MYGICA_TD312: - case PEAK_102569AGPK: - case PROLECTRIX_DV107669: - case SVEON_STV20: - case TERRATEC_CINERGY_T_REV1: - case TERRATEC_CINERGY_T_REV3: - case TERRATEC_NOXON_REV1_B3: - case TERRATEC_NOXON_REV1_B4: - case TERRATEC_NOXON_REV1_B7: - case TERRATEC_NOXON_REV1_C6: - case TERRATEC_NOXON_REV2: - case TERRATEC_T_STICK_PLUS: - case TWINTECH_UT40: - case ZAAPA_ZTMINDVBZP: - return initRTL2832Tuner(tunerClass, device, descriptor); - case UNKNOWN: - default: - break; - } - } - - return new TunerInitStatus(null, "Unknown Device"); - } - - /** - * Releases all tuners in preparation for shutdown - */ - public void releaseTuners() - { - mLog.info("Releasing [" + mTunerModel.getTuners().size() + "] tuner(s) ..."); - Tuner tuner = mTunerModel.getTuner(0); - - while(tuner != null) - { - mTunerModel.removeTuner(tuner); - tuner.dispose(); - tuner = mTunerModel.getTuner(0); - } - } - - private TunerInitStatus initAirspyTuner(Device device, DeviceDescriptor descriptor) - { - try - { - AirspyTunerController airspyController = new AirspyTunerController(device); - - airspyController.init(); - - AirspyTuner tuner = new AirspyTuner(airspyController, mUserPreferences); - - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException se) - { - mLog.error("couldn't construct Airspy controller/tuner", se); - - return new TunerInitStatus(null, - "error constructing Airspy tuner controller"); - } - } - - - private TunerInitStatus initEttusB100Tuner(Device device, DeviceDescriptor descriptor) - { - return new TunerInitStatus(null, "Ettus B100 tuner not currently supported"); - } - - private TunerInitStatus initFuncubeProTuner(Device device, DeviceDescriptor descriptor) - { - String reason = "NOT LOADED"; - - TargetDataLine tdl = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO); - - if(tdl != null) - { - FCD1TunerController controller = new FCD1TunerController(tdl, device, descriptor); - - try - { - controller.init(); - FCDTuner tuner = new FCDTuner(controller, mUserPreferences); - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException e) - { - mLog.error("couldn't load funcube dongle pro tuner", e); - reason = "error during initialization - " + e.getLocalizedMessage(); - } - } - else - { - reason = "couldn't find matching mixer dataline"; - } - - return new TunerInitStatus(null, "Funcube Dongle Pro tuner not loaded - " + reason); - } - - private TunerInitStatus initFuncubeProPlusTuner(Device device, DeviceDescriptor descriptor) - { - String reason = "NOT LOADED"; - - TargetDataLine dataline = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS); - - if(dataline != null) - { - FCD2TunerController controller = new FCD2TunerController(dataline, device, descriptor); - - try - { - controller.init(); - FCDTuner tuner = new FCDTuner(controller, mUserPreferences); - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException e) - { - mLog.error("couldn't load funcube dongle pro plus tuner", e); - reason = "error during initialization - " + e.getLocalizedMessage(); - } - } - else - { - reason = "couldn't find matching mixer dataline"; - } - - return new TunerInitStatus(null, "Funcube Dongle Pro tuner not loaded - " + reason); - } - - private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor) - { - try - { - HackRFTunerController hackRFController = new HackRFTunerController(device, descriptor); - - hackRFController.init(); - - HackRFTuner tuner = new HackRFTuner(hackRFController, mUserPreferences); - - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException se) - { - mLog.error("couldn't construct HackRF controller/tuner", se); - - return new TunerInitStatus(null, "error constructing HackRF tuner controller"); - } - } - - private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass, Device device, DeviceDescriptor deviceDescriptor) - { - String reason = "NOT LOADED"; - - TunerType tunerType = tunerClass.getTunerType(); - - if(tunerType == TunerType.RTL2832_VARIOUS) - { - try - { - tunerType = RTL2832TunerController.identifyTunerType(device); - } - catch(SourceException e) - { - mLog.error("couldn't determine RTL2832 tuner type", e); - tunerType = TunerType.UNKNOWN; - } - } - - switch(tunerType) - { - case ELONICS_E4000: - try - { - E4KTunerController controller = new E4KTunerController(device, deviceDescriptor); - - controller.init(); - - RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller, mUserPreferences); - - return new TunerInitStatus(rtlTuner, "LOADED"); - } - catch(SourceException se) - { - return new TunerInitStatus(null, "Error constructing E4K tuner controller - " + - se.getLocalizedMessage()); - } - case RAFAELMICRO_R820T: - try - { - R820TTunerController controller = new R820TTunerController(device, deviceDescriptor); - - controller.init(); - - RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller, mUserPreferences); - - return new TunerInitStatus(rtlTuner, "LOADED"); - } - catch(SourceException se) - { - mLog.error("error constructing tuner", se); - - return new TunerInitStatus(null, "Error constructing R820T " - + "tuner controller - " + se.getLocalizedMessage()); - } - case FITIPOWER_FC0012: - case FITIPOWER_FC0013: - case RAFAELMICRO_R828D: - case UNKNOWN: - default: - reason = "SDRTRunk doesn't currently support RTL2832 Dongle with [" + tunerType.toString() + - "] tuner for tuner class[" + tunerClass.toString() + "]"; - - mLog.info(reason); - break; - } - - return new TunerInitStatus(null, reason); - } - - public class TunerInitStatus - { - private Tuner mTuner; - private String mInfo; - - public TunerInitStatus(Tuner tuner, String info) - { - mTuner = tuner; - mInfo = info; - } - - public Tuner getTuner() - { - return mTuner; - } - - public String getInfo() - { - return mInfo; - } - - public boolean isLoaded() - { - return mTuner != null; - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java deleted file mode 100644 index 70dc064f..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.source.tuner; - -import io.github.dsheirer.properties.SystemProperties; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.Source; -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.TunerEvent.Event; -import io.github.dsheirer.source.tuner.channel.ChannelSpecification; -import io.github.dsheirer.source.tuner.channel.TunerChannel; -import io.github.dsheirer.source.tuner.channel.TunerChannelSource; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; -import io.github.dsheirer.source.tuner.recording.RecordingTuner; -import io.github.dsheirer.spectrum.SpectralDisplayPanel; -import io.github.dsheirer.util.ThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.table.AbstractTableModel; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class TunerModel extends AbstractTableModel implements Listener -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger(TunerModel.class); - - public static final int TUNER_TYPE = 0; - public static final int TUNER_ID = 1; - public static final int SAMPLE_RATE = 2; - public static final int FREQUENCY = 3; - public static final int CHANNEL_COUNT = 4; - public static final int FREQUENCY_ERROR = 5; - public static final int MEASURED_FREQUENCY_ERROR = 6; - public static final int SPECTRAL_DISPLAY_NEW = 7; - private static final int MAXIMUM_RECORDING_TUNERS_ALLOWED = 1; - private static final String MHZ = " MHz"; - private static final String[] COLUMNS = {"Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Current PPM", - "Measured Error", "Display"}; - - private List mTuners = new ArrayList<>(); - private List> mTunerEventListeners = new ArrayList<>(); - private DecimalFormat mFrequencyFormat = new DecimalFormat("0.00000"); - private DecimalFormat mSampleRateFormat = new DecimalFormat("0.000"); - private DecimalFormat mFrequencyErrorPPMFormat = new DecimalFormat("0.0"); - - private TunerConfigurationModel mTunerConfigurationModel; - - public TunerModel(TunerConfigurationModel tunerConfigurationModel) - { - mTunerConfigurationModel = tunerConfigurationModel; - } - - public TunerConfigurationModel getTunerConfigurationModel() - { - return mTunerConfigurationModel; - } - - /** - * List of Tuners currently in the model - */ - public List getTuners() - { - return mTuners; - } - - /** - * Sorted list of tuner names - */ - public List getTunerNames() - { - List names = new ArrayList<>(); - - for(Tuner tuner: mTuners) - { - names.add(tuner.getName()); - } - - Collections.sort(names); - - return names; - } - - public Tuner getTuner(int index) - { - if(index < mTuners.size()) - { - return mTuners.get(index); - } - - return null; - } - - /** - * Find a tuner that matches the name argument - * - * @param name of the tuner - * @return named tuner or null - */ - public Tuner getTuner(String name) - { - if(name != null) - { - for(Tuner tuner : mTuners) - { - if(!tuner.hasError() && tuner.getName().equalsIgnoreCase(name)) - { - return tuner; - } - } - } - - return null; - } - - public void addTuners(List Tuners) - { - for(Tuner tuner : Tuners) - { - addTuner(tuner); - } - } - - /** - * Adds the Tuner to this model - */ - public void addTuner(Tuner tuner) - { - if(!mTuners.contains(tuner)) - { - //Get the tuner configuration and apply it to the tuner - this - //call should always produce a tuner configuration - TunerConfiguration config = mTunerConfigurationModel - .getTunerConfiguration(tuner.getTunerType(), tuner.getUniqueID()); - - try - { - tuner.getTunerController().apply(config); - - mTuners.add(tuner); - - int index = mTuners.indexOf(tuner); - - fireTableRowsInserted(index, index); - - tuner.addTunerChangeListener(this); - } - catch(SourceException se) - { - mLog.error("Couldn't apply tuner configuration to tuner - [" - + tuner.getTunerType().name() + "] with id [" - + tuner.getUniqueID() + "] - tuner will not be included"); - } - - } - } - - /** - * Removes the Tuner from this model - */ - public void removeTuner(Tuner tuner) - { - if(mTuners.contains(tuner)) - { - tuner.removeTunerChangeListener(this); - - int index = mTuners.indexOf(tuner); - - mTuners.remove(tuner); - - fireTableRowsDeleted(index, index); - } - } - - /** - * Indicates if a recording tuner can be added to the set of tuners. This is constrained by the maximum - * allowed limit. - * @return true if a recording tuner can be added. - */ - public boolean canAddRecordingTuner() - { - int count = 0; - - for(Tuner tuner: mTuners) - { - if(tuner instanceof RecordingTuner) - { - count++; - } - } - - return count < MAXIMUM_RECORDING_TUNERS_ALLOWED; - } - - public void addListener(Listener listener) - { - mTunerEventListeners.add(listener); - } - - public void removeListener(Listener listener) - { - mTunerEventListeners.remove(listener); - } - - public void broadcast(TunerEvent event) - { - for(Listener listener : mTunerEventListeners) - { - listener.receive(event); - } - } - - /** - * Requests to display the first tuner in this model. Invoke this method - * after all listeners have registered and tuners have been added to this - * model, in order to inform the primary display to use the first tuner. - */ - public void requestFirstTunerDisplay() - { - SystemProperties properties = SystemProperties.getInstance(); - boolean enabled = properties.get(SpectralDisplayPanel.SPECTRAL_DISPLAY_ENABLED, true); - - if(enabled && mTuners.size() > 0) - { - //Hack: the airspy tuner would lockup aperiodically and refuse to produce - //transfer buffers ... delaying registering for buffers for 500 ms seems - //to allow the airspy to stabilize before we start asking for samples. - ThreadPool.SCHEDULED.schedule(new Runnable() - { - @Override - public void run() - { - broadcast(new TunerEvent(mTuners.get(0), Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); - } - }, 500, TimeUnit.MILLISECONDS); - } - else - { - broadcast(new TunerEvent(null, Event.CLEAR_MAIN_SPECTRAL_DISPLAY)); - } - } - - @Override - public void receive(TunerEvent event) - { - if(event.getTuner() != null) - { - int index = mTuners.indexOf(event.getTuner()); - - if(index >= 0) - { - switch(event.getEvent()) - { - case CHANNEL_COUNT: - fireTableCellUpdated(index, CHANNEL_COUNT); - break; - case FREQUENCY_UPDATED: - fireTableCellUpdated(index, FREQUENCY); - mTunerConfigurationModel.tunerFrequencyChanged(event.getTuner()); - break; - case FREQUENCY_ERROR_UPDATED: - fireTableCellUpdated(index, FREQUENCY_ERROR); - break; - case MEASURED_FREQUENCY_ERROR_UPDATED: - fireTableCellUpdated(index, MEASURED_FREQUENCY_ERROR); - break; - case SAMPLE_RATE_UPDATED: - fireTableCellUpdated(index, SAMPLE_RATE); - break; - case REQUEST_MAIN_SPECTRAL_DISPLAY: - fireTableCellUpdated(index, MEASURED_FREQUENCY_ERROR); - break; - case ERROR_STATE: - fireTableCellUpdated(index, CHANNEL_COUNT); - fireTableCellUpdated(index, FREQUENCY); - fireTableCellUpdated(index, FREQUENCY_ERROR); - fireTableCellUpdated(index, MEASURED_FREQUENCY_ERROR); - fireTableCellUpdated(index, SAMPLE_RATE); - break; - default: - break; - } - } - } - - broadcast(event); - } - - @Override - public int getRowCount() - { - return mTuners.size(); - } - - @Override - public int getColumnCount() - { - return COLUMNS.length; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - if(rowIndex < mTuners.size()) - { - Tuner tuner = mTuners.get(rowIndex); - - switch(columnIndex) - { - case TUNER_TYPE: - return tuner.getTunerType().getLabel(); - case TUNER_ID: - return tuner.getUniqueID(); - case SAMPLE_RATE: - if(tuner.hasError()) - { - return tuner.getErrorMessage(); - } - double sampleRate = tuner.getTunerController().getSampleRate(); - return mSampleRateFormat.format(sampleRate / 1E6D) + MHZ; - case FREQUENCY: - if(tuner.hasError()) - { - return tuner.getErrorMessage(); - } - try - { - long frequency = tuner.getTunerController().getFrequency(); - - return mFrequencyFormat.format(frequency / 1E6D) + MHZ; - } - catch(Exception e) - { - return 0; - } - case CHANNEL_COUNT: - if(tuner.hasError()) - { - return tuner.getErrorMessage(); - } - int channelCount = tuner.getChannelSourceManager().getTunerChannelCount(); - return channelCount + " (" + (tuner.getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)"); - case FREQUENCY_ERROR: - if(tuner.hasError()) - { - return tuner.getErrorMessage(); - } - double ppm = tuner.getTunerController().getFrequencyCorrection(); - return mFrequencyErrorPPMFormat.format(ppm); - case MEASURED_FREQUENCY_ERROR: - if(tuner.hasError()) - { - return tuner.getErrorMessage(); - } - if(tuner.getTunerController().hasMeasuredFrequencyError()) - { - StringBuilder sb = new StringBuilder(); - sb.append(tuner.getTunerController().getMeasuredFrequencyError()); - sb.append("Hz ("); - sb.append(mFrequencyErrorPPMFormat.format(tuner.getTunerController().getPPMFrequencyError())); - sb.append("ppm)"); - return sb.toString(); - } - - return ""; - case SPECTRAL_DISPLAY_NEW: - if(tuner.hasError()) - { - return null; - } - else - { - return "New"; - } - default: - break; - } - } - - return null; - } - - @Override - public String getColumnName(int columnIndex) - { - return COLUMNS[columnIndex]; - } - - /** - * Iterates current tuners to get a tuner channel source for the specified frequency and bandwidth - * - * Returns null if no tuner can source the channel - */ - public Source getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification, String preferredTuner) - { - TunerChannelSource source = null; - - if(tunerChannel != null && channelSpecification != null) - { - Tuner tuner; - - if(preferredTuner != null) - { - tuner = getTuner(preferredTuner); - - if(tuner != null) - { - try - { - source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification); - - if(source != null) - { - return source; - } - } - catch(Exception e) - { - //Fall through to logger below - } - } - - mLog.info("Unable to source channel [" + tunerChannel.getFrequency() + "] from preferred tuner [" + - preferredTuner + "] - searching for another tuner"); - } - - Iterator it = mTuners.iterator(); - - while(it.hasNext() && source == null) - { - tuner = it.next(); - - if(!tuner.hasError()) - { - try - { - source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification); - } - catch(Exception e) - { - mLog.error("Error obtaining channel from tuner [" + tuner.getName() + "]", e); - } - } - } - } - - return source; - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerSpectralDisplayManager.java b/src/main/java/io/github/dsheirer/source/tuner/TunerSpectralDisplayManager.java deleted file mode 100644 index 0915b3fd..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerSpectralDisplayManager.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.source.tuner; - -import io.github.dsheirer.playlist.PlaylistManager; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.spectrum.SpectralDisplayPanel; -import io.github.dsheirer.spectrum.SpectrumFrame; - -import java.awt.EventQueue; - -public class TunerSpectralDisplayManager implements Listener -{ - private SpectralDisplayPanel mSpectralDisplayPanel; - private PlaylistManager mPlaylistManager; - private SettingsManager mSettingsManager; - private TunerModel mTunerModel; - - public TunerSpectralDisplayManager(SpectralDisplayPanel panel, PlaylistManager playlistManager, - SettingsManager settingsManager, TunerModel tunerModel) - { - mSpectralDisplayPanel = panel; - mPlaylistManager = playlistManager; - mSettingsManager = settingsManager; - mTunerModel = tunerModel; - } - - @Override - public void receive(TunerEvent event) - { - switch(event.getEvent()) - { - case CLEAR_MAIN_SPECTRAL_DISPLAY: - mSpectralDisplayPanel.clearTuner(); - break; - case REQUEST_MAIN_SPECTRAL_DISPLAY: - mSpectralDisplayPanel.showTuner(event.getTuner()); - break; - case REQUEST_NEW_SPECTRAL_DISPLAY: - final SpectrumFrame frame = new SpectrumFrame(mPlaylistManager, mSettingsManager, mTunerModel, event.getTuner()); - EventQueue.invokeLater(() -> frame.setVisible(true)); - break; - default: - break; - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerType.java b/src/main/java/io/github/dsheirer/source/tuner/TunerType.java index 43a4631e..c8686e08 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerType.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerType.java @@ -1,54 +1,95 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner; +import java.util.EnumSet; + +/** + * List of known and optionally supported tuner types + */ public enum TunerType { - AIRSPY_R820T( "Airspy R820T" ), - ELONICS_E4000( "E4000" ), - ETTUS_WBX( "WBX" ), - ETTUS_VARIOUS( "Ettus Tuner" ), - FCI_FC2580( "FC2580" ), - FITIPOWER_FC0012( "FC0012" ), - FITIPOWER_FC0013( "FC0013" ), - FUNCUBE_DONGLE_PRO( "Funcube Dongle Pro" ), - FUNCUBE_DONGLE_PRO_PLUS( "Funcube Dongle Pro Plus" ), - HACKRF( "HackRF" ), - RAFAELMICRO_R820T( "R820T" ), - RAFAELMICRO_R828D( "R828D" ), - RTL2832_VARIOUS( "Generic" ), - TEST("Test"), - RECORDING("Recording"), - UNKNOWN( "Unknown" ); - - private String mLabel; - - private TunerType( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } + AIRSPY_R820T("R820T"), + ELONICS_E4000("E4000"), + FCI_FC2580("FC2580"), + FITIPOWER_FC0012("FC0012"), + FITIPOWER_FC0013("FC0013"), + FUNCUBE_DONGLE_PRO("Funcube Dongle Pro"), + FUNCUBE_DONGLE_PRO_PLUS("Funcube Dongle Pro Plus"), + HACKRF_ONE("ONE"), + HACKRF_JAWBREAKER("Jawbreaker"), + HACKRF_RAD1O("RAD1O"), + RAFAELMICRO_R820T("R820T"), + RAFAELMICRO_R828D("R828D"), + TEST("Test"), + RECORDING("Recording"), + UNKNOWN("Unknown"); + + private String mLabel; + + /** + * Constructs an instance + * + * @param label pretty + */ + TunerType(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + + + /** + * Supported USB tuner types + */ + public static EnumSet SUPPORTED_USB_TUNERS = EnumSet.of(AIRSPY_R820T, ELONICS_E4000, FITIPOWER_FC0012, + HACKRF_JAWBREAKER, HACKRF_RAD1O, HACKRF_ONE, RAFAELMICRO_R820T); + + /** + * Supported sound card tuner types + */ + public static EnumSet SUPPORTED_SOUND_CARD_TUNERS = EnumSet.of(FUNCUBE_DONGLE_PRO, FUNCUBE_DONGLE_PRO_PLUS); + + /** + * Indicates if this tuner is supported as a USB tuner + */ + public boolean isSupportedUsbTuner() + { + return SUPPORTED_USB_TUNERS.contains(this); + } + + /** + * Indicates if this tuner type is supported as a sound card tuner + */ + public boolean isSupportedSoundCardTuner() + { + return SUPPORTED_SOUND_CARD_TUNERS.contains(this); + } + + @Override + public String toString() + { + return getLabel(); + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java deleted file mode 100644 index ffd5205c..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ - -package io.github.dsheirer.source.tuner; - -import com.jidesoft.swing.JideSplitPane; -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.tuner.TunerEvent.Event; -import io.github.dsheirer.source.tuner.recording.AddRecordingTunerDialog; -import io.github.dsheirer.source.tuner.recording.RecordingTuner; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.RowSorter; -import javax.swing.SortOrder; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableRowSorter; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; - -public class TunerViewPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(TunerViewPanel.class); - private static final String TABLE_PREFERENCE_KEY = "tuner.view.panel"; - - private TunerModel mTunerModel; - private JTable mTunerTable; - private JTableColumnWidthMonitor mColumnWidthMonitor; - private TableRowSorter mRowSorter; - private JideSplitPane mSplitPane; - private TunerEditor mTunerEditor; - private UserPreferences mUserPreferences; - - public TunerViewPanel(TunerModel tunerModel, UserPreferences userPreferences) - { - mTunerModel = tunerModel; - mTunerEditor = new TunerEditor(mTunerModel.getTunerConfigurationModel(), userPreferences); - mUserPreferences = userPreferences; - - init(); - } - - private void init() - { - setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow]", "[fill,grow]")); - - mRowSorter = new TableRowSorter<>(mTunerModel); - List sortKeys = new ArrayList<>(); - sortKeys.add(new RowSorter.SortKey(TunerModel.TUNER_TYPE, SortOrder.ASCENDING)); - sortKeys.add(new RowSorter.SortKey(TunerModel.TUNER_ID, SortOrder.ASCENDING)); - mRowSorter.setSortKeys(sortKeys); - - mTunerTable = new JTable(mTunerModel); - mTunerTable.setRowSorter(mRowSorter); - mTunerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - mTunerTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() - { - @Override - public void valueChanged(ListSelectionEvent event) - { - if(!event.getValueIsAdjusting()) - { - int row = mTunerTable.getSelectedRow(); - - if(row >= 0) - { - int modelRow = mTunerTable.convertRowIndexToModel(row); - mTunerEditor.setItem(mTunerModel.getTuner(modelRow)); - } - } - } - }); - - mTunerModel.addListener(new Listener() - { - @Override - public void receive(TunerEvent tunerEvent) - { - switch(tunerEvent.getEvent()) - { - case LOCK_STATE_CHANGE: - if(tunerEvent.getTuner() != null) - { - int row = mTunerTable.getSelectedRow(); - - if(row >= 0) - { - int modelRow = mTunerTable.convertRowIndexToModel(row); - Tuner selectedTuner = mTunerModel.getTuner(modelRow); - - if(selectedTuner == tunerEvent.getTuner()) - { - mTunerEditor.setTunerLockState(selectedTuner.getTunerController().isLocked()); - } - } - } - break; - } - } - }); - - mTunerTable.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - if(e.getButton() == MouseEvent.BUTTON1) //Left-Click - { - int column = mTunerTable.columnAtPoint(e.getPoint()); - - if(column == TunerModel.SPECTRAL_DISPLAY_NEW) - { - int tableRow = mTunerTable.rowAtPoint(e.getPoint()); - int modelRow = mTunerTable.convertRowIndexToModel(tableRow); - - Tuner tuner = mTunerModel.getTuner(modelRow); - - if(tuner != null) - { - mTunerModel.broadcast(new TunerEvent(tuner, - Event.REQUEST_NEW_SPECTRAL_DISPLAY)); - } - } - } - else if(e.getButton() == MouseEvent.BUTTON3) //Right-Click - { - int tableRow = mTunerTable.rowAtPoint(e.getPoint()); - int modelRow = mTunerTable.convertRowIndexToModel(tableRow); - - final Tuner tuner = mTunerModel.getTuner(modelRow); - - if(tuner instanceof RecordingTuner && tuner.getChannelSourceManager().getTunerChannelCount() == 0) - { - JPopupMenu popupMenu = new JPopupMenu(); - JMenuItem removeTunerItem = new JMenuItem("Remove Recording Tuner"); - removeTunerItem.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - mTunerModel.removeTuner(tuner); - } - }); - popupMenu.add(removeTunerItem); - popupMenu.show(mTunerTable, e.getX(), e.getY()); - } - } - } - }); - - TableCellRenderer linkCellRenderer = new LinkCellRenderer(); - mTunerTable.getColumnModel().getColumn(TunerModel.SPECTRAL_DISPLAY_NEW).setCellRenderer(linkCellRenderer); - - TableCellRenderer errorCellRenderer = new ErrorCellRenderer(); - mTunerTable.getColumnModel().getColumn(TunerModel.SAMPLE_RATE).setCellRenderer(errorCellRenderer); - mTunerTable.getColumnModel().getColumn(TunerModel.FREQUENCY).setCellRenderer(errorCellRenderer); - mTunerTable.getColumnModel().getColumn(TunerModel.CHANNEL_COUNT).setCellRenderer(errorCellRenderer); - mTunerTable.getColumnModel().getColumn(TunerModel.FREQUENCY_ERROR).setCellRenderer(errorCellRenderer); - mTunerTable.getColumnModel().getColumn(TunerModel.MEASURED_FREQUENCY_ERROR).setCellRenderer(errorCellRenderer); - - mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTunerTable, TABLE_PREFERENCE_KEY); - JScrollPane tunerTableScroller = new JScrollPane(mTunerTable); - tunerTableScroller.setPreferredSize(new Dimension(400, 20)); - - JPanel tunerTablePanel = new JPanel(); - tunerTablePanel.setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow][]", "[fill,grow][]")); - tunerTablePanel.add(tunerTableScroller, "span"); - - tunerTablePanel.add(new JLabel("")); //Empty spacer - JButton addRecordingTunerButton = new JButton("Add Recording Tuner"); - addRecordingTunerButton.setEnabled(mTunerModel.canAddRecordingTuner()); - addRecordingTunerButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - AddRecordingTunerDialog dialog = new AddRecordingTunerDialog(mUserPreferences, mTunerModel); - dialog.setLocationRelativeTo(TunerViewPanel.this); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - dialog.setVisible(true); - } - }); - } - }); - tunerTablePanel.add(addRecordingTunerButton); - - tunerTablePanel.add(addRecordingTunerButton); - - JScrollPane editorScroller = new JScrollPane(mTunerEditor); - editorScroller.setPreferredSize(new Dimension(400, 80)); - - mSplitPane = new JideSplitPane(); - mSplitPane.setOrientation(JideSplitPane.VERTICAL_SPLIT); - mSplitPane.add(tunerTablePanel); - mSplitPane.add(editorScroller); - - add(mSplitPane); - } - - public class ErrorCellRenderer extends DefaultTableCellRenderer - { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - Tuner tuner = mTunerModel.getTuner(mTunerTable.convertRowIndexToModel(row)); - - if(isSelected) - { - component.setBackground(table.getSelectionBackground()); - } - else if(tuner.hasError()) - { - component.setBackground(Color.RED); - } - else - { - component.setBackground(table.getBackground()); - } - - return component; - } - } - - public class LinkCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; - - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, - value, isSelected, hasFocus, row, column); - - label.setForeground(Color.BLUE.brighter()); - label.setToolTipText("Show this tuner in a new spectral display"); - - return label; - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java index 7a980910..17014fac 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java @@ -1,43 +1,59 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.airspy; -import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.source.ChannelizerType; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.airspy.AirspyTunerController.BoardID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Airspy Tuner + */ public class AirspyTuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger(AirspyTuner.class); - public AirspyTuner(AirspyTunerController controller, UserPreferences userPreferences) + /** + * Constructs an instance + * @param controller for the airspy + * @param tunerErrorListener to listen for errors from this tuner + * @param channelizerType for the channelizer + */ + public AirspyTuner(AirspyTunerController controller, ITunerErrorListener tunerErrorListener, + ChannelizerType channelizerType) { - super("Airspy " + controller.getDeviceInfo().getSerialNumber(), controller, userPreferences); + super(controller, tunerErrorListener, channelizerType); } + @Override + public String getPreferredName() + { + return "Airspy " + getController().getDeviceInfo().getSerialNumber(); + } + + /** + * Airspy tuner controller + */ public AirspyTunerController getController() { return (AirspyTunerController)getTunerController(); @@ -64,12 +80,6 @@ public class AirspyTuner extends Tuner return TunerClass.AIRSPY; } - @Override - public TunerType getTunerType() - { - return TunerType.AIRSPY_R820T; - } - @Override public double getSampleSize() { diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java index 4c5ac346..05248028 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java @@ -1,18 +1,20 @@ /* - * ********************************************************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * ********************************************************************************************************************* + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.airspy; @@ -23,120 +25,107 @@ import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; public class AirspyTunerConfiguration extends TunerConfiguration { - private int mSampleRate = AirspyTunerController.DEFAULT_SAMPLE_RATE.getRate(); - private Gain mGain = AirspyTunerController.LINEARITY_GAIN_DEFAULT; - private int mIFGain = AirspyTunerController.IF_GAIN_DEFAULT; - private int mMixerGain = AirspyTunerController.MIXER_GAIN_DEFAULT; - private int mLNAGain = AirspyTunerController.LNA_GAIN_DEFAULT; - private double mFrequencyCorrection = 0.0d; - - private boolean mMixerAGC = false; - private boolean mLNAAGC = false; + private Gain mGain = AirspyTunerController.LINEARITY_GAIN_DEFAULT; + private int mSampleRate = AirspyTunerController.DEFAULT_SAMPLE_RATE.getRate(); + private int mIFGain = AirspyTunerController.IF_GAIN_DEFAULT; + private int mMixerGain = AirspyTunerController.MIXER_GAIN_DEFAULT; + private int mLNAGain = AirspyTunerController.LNA_GAIN_DEFAULT; + private boolean mMixerAGC = false; + private boolean mLNAAGC = false; - /** - * Default constructor for JAXB - */ - public AirspyTunerConfiguration() - { - } - - public AirspyTunerConfiguration( String uniqueID, String name ) - { - super( uniqueID, name ); - } - - @Override - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") - public TunerType getTunerType() + /** + * Default constructor for JAXB + */ + public AirspyTunerConfiguration() { - return TunerType.AIRSPY_R820T; } - @JacksonXmlProperty(isAttribute = true, localName = "sample_rate") - public int getSampleRate() - { - return mSampleRate; - } - - public void setSampleRate( int sampleRate ) - { - mSampleRate = sampleRate; - } + @Override + @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + public TunerType getTunerType() + { + return TunerType.AIRSPY_R820T; + } - @JacksonXmlProperty(isAttribute = true, localName = "gain") - public Gain getGain() - { - return mGain; - } - - public void setGain( Gain gain ) - { - mGain = gain; - } + public AirspyTunerConfiguration(String uniqueID) + { + super(uniqueID); + } - @JacksonXmlProperty(isAttribute = true, localName = "if_gain") - public int getIFGain() - { - return mIFGain; - } - - public void setIFGain( int gain ) - { - mIFGain = gain; - } + @JacksonXmlProperty(isAttribute = true, localName = "sample_rate") + public int getSampleRate() + { + return mSampleRate; + } - @JacksonXmlProperty(isAttribute = true, localName = "mixer_gain") - public int getMixerGain() - { - return mMixerGain; - } - - public void setMixerGain( int gain ) - { - mMixerGain = gain; - } + public void setSampleRate(int sampleRate) + { + mSampleRate = sampleRate; + } - @JacksonXmlProperty(isAttribute = true, localName = "lna_gain") - public int getLNAGain() - { - return mLNAGain; - } - - public void setLNAGain( int gain ) - { - mLNAGain = gain; - } + @JacksonXmlProperty(isAttribute = true, localName = "gain") + public Gain getGain() + { + return mGain; + } - @JacksonXmlProperty(isAttribute = true, localName = "mixer_agc") - public boolean isMixerAGC() - { - return mMixerAGC; - } - - public void setMixerAGC( boolean enabled ) - { - mMixerAGC = enabled; - } + public void setGain(Gain gain) + { + mGain = gain; + } - @JacksonXmlProperty(isAttribute = true, localName = "lna_agc") - public boolean isLNAAGC() - { - return mLNAAGC; - } - - public void setLNAAGC( boolean enabled ) - { - mLNAAGC = enabled; - } + @JacksonXmlProperty(isAttribute = true, localName = "if_gain") + public int getIFGain() + { + return mIFGain; + } - @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection( double value ) - { - mFrequencyCorrection = value; - } + public void setIFGain(int gain) + { + mIFGain = gain; + } + + @JacksonXmlProperty(isAttribute = true, localName = "mixer_gain") + public int getMixerGain() + { + return mMixerGain; + } + + public void setMixerGain(int gain) + { + mMixerGain = gain; + } + + @JacksonXmlProperty(isAttribute = true, localName = "lna_gain") + public int getLNAGain() + { + return mLNAGain; + } + + public void setLNAGain(int gain) + { + mLNAGain = gain; + } + + @JacksonXmlProperty(isAttribute = true, localName = "mixer_agc") + public boolean isMixerAGC() + { + return mMixerAGC; + } + + public void setMixerAGC(boolean enabled) + { + mMixerAGC = enabled; + } + + @JacksonXmlProperty(isAttribute = true, localName = "lna_agc") + public boolean isLNAAGC() + { + return mLNAAGC; + } + + public void setLNAAGC(boolean enabled) + { + mLNAAGC = enabled; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java index cea290f5..c7859894 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java @@ -19,16 +19,16 @@ package io.github.dsheirer.source.tuner.airspy; +import io.github.dsheirer.buffer.INativeBufferFactory; import io.github.dsheirer.buffer.airspy.AirspyNativeBufferFactory; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; +import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.usb.USBTransferProcessor; import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.apache.commons.io.EndianUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; @@ -39,10 +39,13 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; -import java.util.concurrent.locks.ReentrantLock; +/** + * Airspy tuner controller + */ public class AirspyTunerController extends USBTunerController { + private final static Logger mLog = LoggerFactory.getLogger(AirspyTunerController.class); public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14; public static final Gain SENSITIVITY_GAIN_DEFAULT = Gain.SENSITIVITY_10; public static final int GAIN_MIN = 1; @@ -60,32 +63,44 @@ public class AirspyTunerController extends USBTunerController public static final long FREQUENCY_MAX = 1800000000l; public static final long FREQUENCY_DEFAULT = 101100000; public static final double USABLE_BANDWIDTH_PERCENT = 0.90; - public static final AirspySampleRate DEFAULT_SAMPLE_RATE = new AirspySampleRate(0, 10000000, "10.00 MHz"); - private static final long USB_TIMEOUT_MS = 2000l; //milliseconds - private static final byte USB_INTERFACE = (byte) 0x0; private static final int USB_TRANSFER_BUFFER_SIZE = 262144; private static final byte USB_REQUEST_IN = (byte) (LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); private static final byte USB_REQUEST_OUT = (byte) (LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); public static final DecimalFormat MHZ_FORMATTER = new DecimalFormat("#.00 MHz"); - - private final static Logger mLog = LoggerFactory.getLogger(AirspyTunerController.class); - - private Device mDevice; - private DeviceHandle mDeviceHandle; - + public static final AirspySampleRate DEFAULT_SAMPLE_RATE = new AirspySampleRate(0, 10000000, "10.00 MHz"); private AirspyNativeBufferFactory mNativeBufferFactory = new AirspyNativeBufferFactory(); private AirspyDeviceInformation mDeviceInfo; private List mSampleRates = new ArrayList<>(); private int mSampleRate = 0; - private ReentrantLock mLock = new ReentrantLock(); - private USBTransferProcessor mUSBTransferProcessor; - public AirspyTunerController(Device device) throws SourceException + /** + * Constructs an instance + * @param bus usb + * @param port usb + * @param tunerErrorListener to receive error notifications from this controller + */ + public AirspyTunerController(int bus, int port, ITunerErrorListener tunerErrorListener) { - super(FREQUENCY_MIN, FREQUENCY_MAX, 0, USABLE_BANDWIDTH_PERCENT); + super(bus, port, FREQUENCY_MIN, FREQUENCY_MAX, 0, USABLE_BANDWIDTH_PERCENT, tunerErrorListener); + } - mDevice = device; + @Override + public TunerType getTunerType() + { + return TunerType.AIRSPY_R820T; + } + + @Override + protected INativeBufferFactory getNativeBufferFactory() + { + return mNativeBufferFactory; + } + + @Override + protected int getTransferBufferSize() + { + return USB_TRANSFER_BUFFER_SIZE; } @Override @@ -95,35 +110,9 @@ public class AirspyTunerController extends USBTunerController return USB_TRANSFER_BUFFER_SIZE / 4; } - public void init() throws SourceException + @Override + protected void deviceStart() throws SourceException { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) - { - if(result == LibUsb.ERROR_ACCESS) - { - mLog.error("Unable to access Airspy - insufficient permissions." - + " If you are running a Linux OS, have you installed the " - + "airspy rules file in \\etc\\udev\\rules.d ??"); - } - - throw new SourceException("Couldn't open airspy device - " + LibUsb.strError(result)); - } - - try - { - claimInterface(); - } - catch(Exception e) - { - throw new SourceException("Airspy Tuner Controller - error while " - + "setting USB configuration, claiming USB interface or " - + "reset the kernel mode driver", e); - } - try { setSamplePacking(false); @@ -161,122 +150,30 @@ public class AirspyTunerController extends USBTunerController { mLog.error("Setting sample rate is not supported by firmware", e); } - - String deviceName = "Airspy " + getDeviceInfo().getSerialNumber(); - - mUSBTransferProcessor = new USBTransferProcessor(deviceName, mDeviceHandle, mNativeBufferFactory, - USB_TRANSFER_BUFFER_SIZE, this); } @Override - public void dispose() + protected void deviceStop() { - if(mDeviceHandle != null) + try { - mLog.info("Releasing Airspy Tuner - " + getDeviceInfo().getSerialNumber()); - try - { - LibUsb.close(mDeviceHandle); - } - catch(Exception e) - { - mLog.error("error while closing device handle", e); - } - - mDeviceHandle = null; + setReceiverMode(false); + } + catch(Exception e) + { + mLog.error("Couldn't enable airspy receiver mode", e); } } @Override - protected USBTransferProcessor getUSBTransferProcessor() + public void apply(TunerConfiguration tunerConfiguration) throws SourceException { - return mUSBTransferProcessor; - } + //Invoke super for frequency, frequency correction and autoPPM + super.apply(tunerConfiguration); - - /** - * Claims the USB interface. If another application currently has - * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting - * will dictate if the interface is forcibly claimed from the other - * application - */ - private void claimInterface() throws SourceException - { - if(mDeviceHandle != null) + if(tunerConfiguration instanceof AirspyTunerConfiguration config) { - int result = LibUsb.kernelDriverActive(mDeviceHandle, USB_INTERFACE); - - if(result == 1) - { - - result = LibUsb.detachKernelDriver(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + - LibUsb.errorName(result) + "]"); - - throw new SourceException("couldn't detach kernel driver " - + "from device"); - } - } - - result = LibUsb.setConfiguration(mDeviceHandle, 1); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't set USB configuration 1 [" + - LibUsb.errorName(result) + "]"); - } - - result = LibUsb.claimInterface(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + - LibUsb.errorName(result) + "]"); - } - } - else - { - throw new SourceException("couldn't claim usb interface - no " - + "device handle"); - } - } - - public static String getTransferStatus(int status) - { - switch(status) - { - case 0: - return "TRANSFER COMPLETED (0)"; - case 1: - return "TRANSFER ERROR (1)"; - case 2: - return "TRANSFER TIMED OUT (2)"; - case 3: - return "TRANSFER CANCELLED (3)"; - case 4: - return "TRANSFER STALL (4)"; - case 5: - return "TRANSFER NO DEVICE (5)"; - case 6: - return "TRANSFER OVERFLOW (6)"; - default: - return "UNKNOWN TRANSFER STATUS (" + status + ")"; - } - } - - - @Override - public void apply(TunerConfiguration config) throws SourceException - { - if(config instanceof AirspyTunerConfiguration) - { - AirspyTunerConfiguration airspy = (AirspyTunerConfiguration) config; - - int sampleRate = airspy.getSampleRate(); - + int sampleRate = config.getSampleRate(); AirspySampleRate rate = getSampleRate(sampleRate); if(rate == null) @@ -302,39 +199,28 @@ public class AirspyTunerController extends USBTunerController try { - setIFGain(airspy.getIFGain()); - setMixerGain(airspy.getMixerGain()); - setLNAGain(airspy.getLNAGain()); + setFrequency(config.getFrequency()); + setFrequencyCorrection(config.getFrequencyCorrection()); + getFrequencyErrorCorrectionManager().setEnabled(config.getAutoPPMCorrectionEnabled()); - setMixerAGC(airspy.isMixerAGC()); - setLNAAGC(airspy.isLNAAGC()); + setIFGain(config.getIFGain()); + setMixerGain(config.getMixerGain()); + setLNAGain(config.getLNAGain()); + setMixerAGC(config.isMixerAGC()); + setLNAAGC(config.isLNAAGC()); - //Set the gain mode last, so custom values are already set, and - //linearity and sensitivity modes will automatically override - //the custom values. - setGain(airspy.getGain()); - - setFrequencyCorrection(airspy.getFrequencyCorrection()); + //Set the gain mode last, so custom values are already set, and linearity and sensitivity modes will + //automatically override the custom values. + setGain(config.getGain()); } catch(Exception e) { throw new SourceException("Couldn't apply gain settings from airspy config", e); } - - try - { - setFrequency(airspy.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } else { - throw new IllegalArgumentException("Invalid tuner config:" + - config.getClass()); + throw new IllegalArgumentException("Invalid tuner config:" + tunerConfiguration.getClass()); } } @@ -345,37 +231,28 @@ public class AirspyTunerController extends USBTunerController } @Override - public void setTunedFrequency(long frequency) throws SourceException + public synchronized void setTunedFrequency(long frequency) throws SourceException { - mLock.lock(); - - try + if(FREQUENCY_MIN <= frequency && frequency <= FREQUENCY_MAX) { - if(FREQUENCY_MIN <= frequency && frequency <= FREQUENCY_MAX) - { - ByteBuffer buffer = ByteBuffer.allocateDirect(4); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.putInt((int) frequency); - buffer.rewind(); + ByteBuffer buffer = ByteBuffer.allocateDirect(4); + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.putInt((int) frequency); + buffer.rewind(); - try - { - write(Command.SET_FREQUENCY, 0, 0, buffer); - } - catch(UsbException e) - { - mLog.error("error setting frequency [" + frequency + "]", e); - throw new SourceException("error setting frequency [" + frequency + "]", e); - } - } - else + try { - throw new SourceException("Frequency [" + frequency + "] outside " + "of tunable range " + FREQUENCY_MIN + "-" + FREQUENCY_MAX); + write(Command.SET_FREQUENCY, 0, 0, buffer); + } + catch(UsbException e) + { + mLog.error("error setting frequency [" + frequency + "]", e); + throw new SourceException("error setting frequency [" + frequency + "]", e); } } - finally + else { - mLock.unlock(); + throw new SourceException("Frequency [" + frequency + "] outside " + "of tunable range " + FREQUENCY_MIN + "-" + FREQUENCY_MAX); } } @@ -386,13 +263,11 @@ public class AirspyTunerController extends USBTunerController } /** - * Sets the sample rate to the rate specified by the index value in the - * available sample rates map + * Sets the sample rate to the rate specified by the index value in the available sample rates map * * @param rate to a sample rate in the available samples rates map. * @throws IllegalArgumentException if index is not a valid rate index - * @throws LibUsbException if there was a read error or if this operation - * is not supported by the current firmware + * @throws LibUsbException if there was a read error or if this operation is not supported by the current firmware * @throws UsbException if there was a USB error */ public void setSampleRate(AirspySampleRate rate) throws LibUsbException, UsbException, SourceException @@ -468,15 +343,13 @@ public class AirspyTunerController extends USBTunerController } /** - * Enables/Disables sample packing to allow two 12-bit samples to be packed - * into 3 bytes (enabled) or 4 bytes (disabled). + * Enables/Disables sample packing to allow two 12-bit samples to be packed into 3 bytes (enabled) or 4 bytes (disabled). * - * @param enabled - * @throws UsbException if sample packing is not supported by the current - * device firmware or if there were usb communication issues + * @param enabled true or false + * @throws UsbException if sample packing is not supported by the current device firmware or if there were usb + * communication issues */ - public void setSamplePacking(boolean enabled) - throws LibUsbException, UsbException + public void setSamplePacking(boolean enabled) throws LibUsbException, UsbException { int result = readByte(Command.SET_PACKING, 0, (enabled ? 1 : 0), true); @@ -497,22 +370,13 @@ public class AirspyTunerController extends USBTunerController * @throws LibUsbException on unsuccessful read operation * @throws UsbException on USB error */ - public void setMixerAGC(boolean enabled) throws LibUsbException, UsbException + public synchronized void setMixerAGC(boolean enabled) throws LibUsbException, UsbException { - mLock.lock(); + int result = readByte(Command.SET_MIXER_AGC, 0, (enabled ? 1 : 0), true); - try + if(result != LibUsb.SUCCESS) { - int result = readByte(Command.SET_MIXER_AGC, 0, (enabled ? 1 : 0), true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set mixer AGC enabled: " + enabled); - } - } - finally - { - mLock.unlock();; + throw new UsbException("Couldnt set mixer AGC enabled: " + enabled); } } @@ -523,22 +387,13 @@ public class AirspyTunerController extends USBTunerController * @throws LibUsbException on unsuccessful read operation * @throws UsbException on USB error */ - public void setLNAAGC(boolean enabled) throws LibUsbException, UsbException + public synchronized void setLNAAGC(boolean enabled) throws LibUsbException, UsbException { - mLock.lock(); + int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true); - try + if(result != LibUsb.SUCCESS) { - int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set LNA AGC enabled: " + enabled); - } - } - finally - { - mLock.unlock(); + throw new UsbException("Couldn't set LNA AGC enabled: " + enabled); } } @@ -562,30 +417,21 @@ public class AirspyTunerController extends USBTunerController * @throws UsbException on error in USB transfer * @throws IllegalArgumentException if gain value is invalid */ - public void setLNAGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException + public synchronized void setLNAGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException { - mLock.lock(); - - try + if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX) { - if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX) - { - int result = readByte(Command.SET_LNA_GAIN, 0, gain, true); + int result = readByte(Command.SET_LNA_GAIN, 0, gain, true); - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set LNA gain to: " + gain); - } - } - else + if(result != LibUsb.SUCCESS) { - throw new IllegalArgumentException("LNA gain value [" + gain + - "] is outside value range: " + LNA_GAIN_MIN + "-" + LNA_GAIN_MAX); + throw new UsbException("Couldnt set LNA gain to: " + gain); } } - finally + else { - mLock.unlock(); + throw new IllegalArgumentException("LNA gain value [" + gain + "] is outside value range: " + + LNA_GAIN_MIN + "-" + LNA_GAIN_MAX); } } @@ -599,28 +445,19 @@ public class AirspyTunerController extends USBTunerController */ public void setMixerGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException { - mLock.lock(); - - try + if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX) { - if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX) - { - int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true); + int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true); - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set mixer gain to: " + gain); - } - } - else + if(result != LibUsb.SUCCESS) { - throw new IllegalArgumentException("Mixer gain value [" + gain + - "] is outside value range: " + MIXER_GAIN_MIN + "-" + MIXER_GAIN_MAX); + throw new UsbException("Couldnt set mixer gain to: " + gain); } } - finally + else { - mLock.unlock(); + throw new IllegalArgumentException("Mixer gain value [" + gain + + "] is outside value range: " + MIXER_GAIN_MIN + "-" + MIXER_GAIN_MAX); } } @@ -634,28 +471,19 @@ public class AirspyTunerController extends USBTunerController */ public void setIFGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException { - mLock.lock(); - - try + if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX) { - if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX) - { - int result = readByte(Command.SET_VGA_GAIN, 0, gain, true); + int result = readByte(Command.SET_VGA_GAIN, 0, gain, true); - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set VGA gain to: " + gain); - } - } - else + if(result != LibUsb.SUCCESS) { - throw new IllegalArgumentException("VGA gain value [" + gain + - "] is outside value range: " + IF_GAIN_MIN + "-" + IF_GAIN_MAX); + throw new UsbException("Couldnt set VGA gain to: " + gain); } } - finally + else { - mLock.unlock(); + throw new IllegalArgumentException("VGA gain value [" + gain + + "] is outside value range: " + IF_GAIN_MIN + "-" + IF_GAIN_MAX); } } @@ -663,7 +491,6 @@ public class AirspyTunerController extends USBTunerController { //Empty buffer to throw away ByteBuffer buffer = ByteBuffer.allocateDirect(0); - write(Command.RECEIVER_MODE, (enabled ? 1 : 0), 0, buffer); } @@ -743,7 +570,6 @@ public class AirspyTunerController extends USBTunerController try { int boardID = readByte(Command.BOARD_ID_READ, 0, 0, true); - mDeviceInfo.setBoardID(boardID); } catch(LibUsbException | UsbException e) @@ -754,10 +580,9 @@ public class AirspyTunerController extends USBTunerController /* Version String */ try { - //NOTE: libairspy is internally reading 127 bytes, however airspy_info - //script is telling it to read 255 bytes ... things that make you go hmmmm + //NOTE: libairspy is internally reading 127 bytes, however airspy_info script is telling it to read + //255 bytes ... things that make you go hmmmm byte[] version = readArray(Command.VERSION_STRING_READ, 0, 0, 127); - mDeviceInfo.setVersion(version); } catch(LibUsbException | UsbException e) @@ -769,9 +594,7 @@ public class AirspyTunerController extends USBTunerController try { //Read 6 x 32-bit integers = 24 bytes - byte[] serial = readArray( - Command.BOARD_PART_ID_SERIAL_NUMBER_READ, 0, 0, 24); - + byte[] serial = readArray(Command.BOARD_PART_ID_SERIAL_NUMBER_READ, 0, 0, 24); mDeviceInfo.setPartAndSerialNumber(serial); } catch(LibUsbException | UsbException e) @@ -790,20 +613,14 @@ public class AirspyTunerController extends USBTunerController * @throws LibUsbException if the operation is unsuccesful * @throws UsbException on any usb errors */ - private int readByte(Command command, int value, int index, boolean signed) - throws LibUsbException, UsbException + private int readByte(Command command, int value, int index, boolean signed) throws LibUsbException, UsbException { - if(mDeviceHandle != null) + if(isRunning()) { ByteBuffer buffer = ByteBuffer.allocateDirect(1); - int transferred = LibUsb.controlTransfer(mDeviceHandle, - USB_REQUEST_IN, - command.getValue(), - (short) value, - (short) index, - buffer, - USB_TIMEOUT_MS); + int transferred = LibUsb.controlTransfer(getDeviceHandle(), USB_REQUEST_IN, command.getValue(), + (short) value, (short) index, buffer, USB_TIMEOUT_MS); if(transferred < 0) { @@ -821,11 +638,8 @@ public class AirspyTunerController extends USBTunerController return result; } } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } + + return 0; } /** @@ -836,24 +650,17 @@ public class AirspyTunerController extends USBTunerController * @param index - usb packet index * @param length - number of bytes to read * @return - bytes read from the device - * @throws LibUsbException if quantity of bytes read doesn't equal the - * requested number of bytes + * @throws LibUsbException if quantity of bytes read doesn't equal the requested number of bytes * @throws UsbException on error communicating with the device */ - private byte[] readArray(Command command, int value, int index, int length) - throws LibUsbException, UsbException + private byte[] readArray(Command command, int value, int index, int length) throws LibUsbException, UsbException { - if(mDeviceHandle != null) + if(isRunning()) { ByteBuffer buffer = ByteBuffer.allocateDirect(length); - int transferred = LibUsb.controlTransfer(mDeviceHandle, - USB_REQUEST_IN, - command.getValue(), - (short) value, - (short) index, - buffer, - USB_TIMEOUT_MS); + int transferred = LibUsb.controlTransfer(getDeviceHandle(), USB_REQUEST_IN, command.getValue(), (short) value, + (short) index, buffer, USB_TIMEOUT_MS); if(transferred < 0) { @@ -861,16 +668,11 @@ public class AirspyTunerController extends USBTunerController } byte[] results = new byte[transferred]; - buffer.get(results); - return results; } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } + + return new byte[0]; } /** @@ -882,36 +684,23 @@ public class AirspyTunerController extends USBTunerController * @param buffer - data to write to the device * @throws UsbException on error */ - public void write(Command command, int value, int index, ByteBuffer buffer) - throws UsbException + public void write(Command command, int value, int index, ByteBuffer buffer) throws UsbException { - if(mDeviceHandle != null) + if(isRunning()) { - int transferred = LibUsb.controlTransfer(mDeviceHandle, - USB_REQUEST_OUT, - command.getValue(), - (short) value, - (short) index, - buffer, - USB_TIMEOUT_MS); + int transferred = LibUsb.controlTransfer(getDeviceHandle(), USB_REQUEST_OUT, command.getValue(), + (short) value, (short) index, buffer, USB_TIMEOUT_MS); if(transferred < 0) { - throw new LibUsbException("error writing byte buffer", - transferred); + throw new LibUsbException("error writing byte buffer", transferred); } else if(transferred != buffer.capacity()) { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); + throw new LibUsbException("transferred bytes [" + transferred + "] is not what was expected [" + + buffer.capacity() + "]", transferred); } } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } } public enum GainMode @@ -1032,17 +821,13 @@ public class AirspyTunerController extends USBTunerController } } - public static GainMode getGainMode(Gain gain) + public GainMode getGainMode() { - if(gain == CUSTOM) - { - return GainMode.CUSTOM; - } - else if(getLinearityGains().contains(gain)) + if(getLinearityGains().contains(this)) { return GainMode.LINEARITY; } - else if(getSensitivityGains().contains(gain)) + else if(getSensitivityGains().contains(this)) { return GainMode.SENSITIVITY; } @@ -1061,7 +846,6 @@ public class AirspyTunerController extends USBTunerController } } - /** * Airspy Board Identifier */ @@ -1135,7 +919,7 @@ public class AirspyTunerController extends USBTunerController private int mValue; - private Command(int value) + Command(int value) { mValue = value; } @@ -1241,7 +1025,7 @@ public class AirspyTunerController extends USBTunerController private int mValue; - private GPIOPin(int value) + GPIOPin(int value) { mValue = value; } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java index 342e4e87..491ee1ea 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java @@ -1,31 +1,33 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.airspy; -import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.tuner.airspy.AirspyTunerController.Gain; import io.github.dsheirer.source.tuner.airspy.AirspyTunerController.GainMode; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.manager.TunerStatus; +import io.github.dsheirer.source.tuner.ui.TunerEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; @@ -35,416 +37,467 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; import javax.swing.JSlider; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import javax.usb.UsbException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; import java.util.List; -public class AirspyTunerEditor extends TunerConfigurationEditor +/** + * Airspy tuner editor/controller + */ +public class AirspyTunerEditor extends TunerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(AirspyTunerEditor.class); - - private JTextField mConfigurationName; - private JButton mTunerInfo; - + private JButton mTunerInfoButton; private JComboBox mSampleRateCombo; - private JSpinner mFrequencyCorrection; - private JComboBox mGainModeCombo; - - private JSlider mMasterGain; + private JSlider mMasterGainSlider; + private JLabel mMasterGainLabel; private JLabel mMasterGainValueLabel; - - private JSlider mIFGain; + private JSlider mIFGainSlider; + private JLabel mIFGainLabel; private JLabel mIFGainValueLabel; - - private JSlider mLNAGain; + private JSlider mLNAGainSlider; private JLabel mLNAGainValueLabel; - - private JSlider mMixerGain; + private JSlider mMixerGainSlider; private JLabel mMixerGainValueLabel; + private JCheckBox mLNAAGCCheckBox; + private JCheckBox mMixerAGCCheckBox; - private JCheckBox mLNAAGC; - private JCheckBox mMixerAGC; - - private FrequencyCorrectionChangeListener mFrequencyCorrectionChangeListener = new FrequencyCorrectionChangeListener(); - private AirspyTunerController mController; - private boolean mLoading; - - public AirspyTunerEditor(TunerConfigurationModel tunerConfigurationModel, - AirspyTuner tuner) + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager for saving tuner configuration + * @param discoveredTuner for the optionally usable Airspy tuner and controller + */ + public AirspyTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) { - super(tunerConfigurationModel); - mController = tuner.getController(); - + super(userPreferences, tunerManager, discoveredTuner); init(); + tunerStatusUpdated(); } - private AirspyTunerConfiguration getConfiguration() + @Override + protected void tunerStatusUpdated() { - if(hasItem()) + setLoading(true); + + if(hasTuner()) { - return (AirspyTunerConfiguration)getItem(); + getTunerIdLabel().setText(getTuner().getPreferredName()); + } + else + { + getTunerIdLabel().setText(getDiscoveredTuner().getId()); } - return null; + String status = getDiscoveredTuner().getTunerStatus().toString(); + if(getDiscoveredTuner().hasErrorMessage()) + { + status += " - " + getDiscoveredTuner().getErrorMessage(); + } + getTunerStatusLabel().setText(status); + getButtonPanel().updateControls(); + getFrequencyPanel().updateControls(); + getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked()); + getTunerInfoButton().setEnabled(hasTuner()); + updateGainComponents(hasTuner() ? getConfiguration().getGain() : null); + + if(hasTuner()) + { + List rates = getTuner().getController().getSampleRates(); + getSampleRateCombo().setModel(new DefaultComboBoxModel<>(rates.toArray(new AirspySampleRate[rates.size()]))); + AirspySampleRate sampleRate = getSampleRate(getConfiguration().getSampleRate()); + getSampleRateCombo().setSelectedItem(sampleRate); + } + else + { + getSampleRateCombo().setModel(new DefaultComboBoxModel<>()); + } + + setLoading(false); } private void init() { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][grow]")); + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][][][][][][][grow]")); - add(new JLabel("Airspy Tuner Configuration"), "span,align center"); + add(new JLabel("Tuner:")); + add(getTunerIdLabel()); + add(getTunerInfoButton()); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() - { - @Override - public void focusLost(FocusEvent e) - { - save(); - } + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); - @Override - public void focusGained(FocusEvent e) - { - } - }); - - add(new JLabel("Name:")); - add(mConfigurationName, "span 2"); - - mTunerInfo = new JButton("Tuner Info"); - mTunerInfo.setEnabled(false); - mTunerInfo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - JOptionPane.showMessageDialog(AirspyTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(mTunerInfo); - - /** - * Sample Rate - */ - add(new JLabel("Sample Rate:")); - - List rates = mController.getSampleRates(); - - mSampleRateCombo = new JComboBox( - new DefaultComboBoxModel(rates.toArray( - new AirspySampleRate[0]))); - mSampleRateCombo.setEnabled(false); - mSampleRateCombo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem(); - - try - { - mController.setSampleRate(rate); - save(); - } - catch(LibUsbException | UsbException | SourceException e1) - { - JOptionPane.showMessageDialog(AirspyTunerEditor.this, - "Couldn't set sample rate to " + rate.getLabel()); - - mLog.error("Error setting airspy sample rate", e1); - } - } - }); - - add(mSampleRateCombo); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel(0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1); //step - - mFrequencyCorrection = new JSpinner(model); - mFrequencyCorrection.setEnabled(false); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits(1); - editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - - mFrequencyCorrection.addChangeListener(mFrequencyCorrectionChangeListener); - - add(new JLabel("PPM:")); - add(mFrequencyCorrection); + add(getButtonPanel(), "span,align left"); add(new JSeparator(), "span,growx,push"); - /** - * Gain Mode - */ - add(new JLabel("Gain Mode:")); - mGainModeCombo = new JComboBox(GainMode.values()); - mGainModeCombo.setEnabled(false); - mGainModeCombo.addActionListener(new ActionListener() + add(new JLabel("Frequency (MHz):")); + add(getFrequencyPanel(), "wrap"); + + add(new JLabel("Sample Rate:")); + add(getSampleRateCombo(), "wrap"); + + add(new JSeparator(), "span,growx,push"); + add(new JLabel("Gain Control"), "wrap"); + + add(new JLabel("Mode:")); + add(getGainModeCombo(), "wrap"); + + add(getMasterGainLabel()); + add(getMasterGainSlider()); + add(getMasterGainValueLabel()); + + add(getIFGainLabel()); + add(getIFGainSlider()); + add(getIFGainValueLabel()); + + add(getMixerAGCCheckBox()); + add(getMixerGainSlider()); + add(getMixerGainValueLabel()); + + add(getLNAAGCCheckBox()); + add(getLNAGainSlider()); + add(getLNAGainValueLabel()); + } + + private JCheckBox getLNAAGCCheckBox() + { + if(mLNAAGCCheckBox == null) { - @Override - public void actionPerformed(ActionEvent e) + mLNAAGCCheckBox = new JCheckBox("AGC LNA:"); + mLNAAGCCheckBox.setEnabled(false); + mLNAAGCCheckBox.addActionListener(e -> { - GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); - - updateGainComponents(mode); - - if(hasItem()) + if(hasTuner() && !isLoading()) { - AirspyTunerConfiguration airspy = getConfiguration(); - - Gain gain = airspy.getGain(); - - if(mode == GainMode.CUSTOM) + try { - mIFGain.setValue(airspy.getIFGain()); - mMixerGain.setValue(airspy.getMixerGain()); - mMixerAGC.setSelected(airspy.isMixerAGC()); - mLNAGain.setValue(airspy.getLNAGain()); - mLNAAGC.setSelected(airspy.isLNAAGC()); + getTuner().getController().setLNAAGC(getLNAAGCCheckBox().isSelected()); + getLNAGainSlider().setEnabled(!getLNAAGCCheckBox().isSelected()); + save(); } - else + catch(Exception e1) { - mMixerAGC.setSelected(false); - mLNAAGC.setSelected(false); - mMasterGain.setValue(gain.getValue()); - mIFGain.setValue(gain.getIF()); - mMixerGain.setValue(gain.getMixer()); - mLNAGain.setValue(gain.getLNA()); + mLog.error("Error setting LNA AGC Enabled"); + } + } + }); + } + + return mLNAAGCCheckBox; + } + + private JCheckBox getMixerAGCCheckBox() + { + if(mMixerAGCCheckBox == null) + { + mMixerAGCCheckBox = new JCheckBox("AGC Mixer:"); + mMixerAGCCheckBox.setEnabled(false); + mMixerAGCCheckBox.addActionListener(e -> + { + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getController().setMixerAGC(getMixerAGCCheckBox().isSelected()); + getMixerGainSlider().setEnabled(!getMixerAGCCheckBox().isSelected()); + save(); + } + catch(Exception e1) + { + mLog.error("Error setting Mixer AGC Enabled"); + } + } + }); + } + + return mMixerAGCCheckBox; + } + + private JLabel getLNAGainValueLabel() + { + if(mLNAGainValueLabel == null) + { + mLNAGainValueLabel = new JLabel("0"); + mLNAGainValueLabel.setEnabled(false); + } + + return mLNAGainValueLabel; + } + + private JSlider getLNAGainSlider() + { + if(mLNAGainSlider == null) + { + mLNAGainSlider = new JSlider(JSlider.HORIZONTAL, AirspyTunerController.LNA_GAIN_MIN, + AirspyTunerController.LNA_GAIN_MAX, AirspyTunerController.LNA_GAIN_MIN); + mLNAGainSlider.setEnabled(false); + mLNAGainSlider.setMajorTickSpacing(1); + mLNAGainSlider.setPaintTicks(true); + mLNAGainSlider.addChangeListener(event -> + { + int gain = mLNAGainSlider.getValue(); + + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getController().setLNAGain(gain); + save(); + } + catch(Exception e) + { + mLog.error("Couldn't set airspy LNA gain to:" + gain, e); + JOptionPane.showMessageDialog(mLNAGainSlider, "Couldn't set LNA gain value to " + gain); } } - save(); - } - }); + getLNAGainValueLabel().setText(String.valueOf(gain)); + }); + } - add(mGainModeCombo, "wrap"); + return mLNAGainSlider; + } - /** - * Gain - */ - add(new JLabel("Master:")); - - mMasterGain = new JSlider(JSlider.HORIZONTAL, - AirspyTunerController.GAIN_MIN, - AirspyTunerController.GAIN_MAX, - AirspyTunerController.GAIN_MIN); - mMasterGain.setMajorTickSpacing(1); - mMasterGain.setPaintTicks(true); - - mMasterGain.addChangeListener(new ChangeListener() + private JLabel getMixerGainValueLabel() + { + if(mMixerGainValueLabel == null) { - @Override - public void stateChanged(ChangeEvent event) + mMixerGainValueLabel = new JLabel("0"); + mMixerGainValueLabel.setEnabled(false); + } + + return mMixerGainValueLabel; + } + + private JSlider getMixerGainSlider() + { + if(mMixerGainSlider == null) + { + mMixerGainSlider = new JSlider(JSlider.HORIZONTAL, AirspyTunerController.MIXER_GAIN_MIN, + AirspyTunerController.MIXER_GAIN_MAX, AirspyTunerController.MIXER_GAIN_MIN); + mMixerGainSlider.setEnabled(false); + mMixerGainSlider.setMajorTickSpacing(1); + mMixerGainSlider.setPaintTicks(true); + mMixerGainSlider.addChangeListener(new ChangeListener() + { + @Override + public void stateChanged(ChangeEvent event) + { + int gain = mMixerGainSlider.getValue(); + + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getController().setMixerGain(gain); + save(); + } + catch(Exception e) + { + mLog.error("Couldn't set airspy Mixer gain to:" + gain, e); + JOptionPane.showMessageDialog(mMixerGainSlider, "Couldn't set Mixer gain value to " + gain); + } + } + + getMixerGainValueLabel().setText(String.valueOf(gain)); + } + }); + } + + return mMixerGainSlider; + } + + private JLabel getIFGainLabel() + { + if(mIFGainLabel == null) + { + mIFGainLabel = new JLabel("IF:"); + } + + return mIFGainLabel; + } + + private JLabel getIFGainValueLabel() + { + if(mIFGainValueLabel == null) + { + mIFGainValueLabel = new JLabel("0"); + mIFGainValueLabel.setEnabled(false); + } + + return mIFGainValueLabel; + } + + private JSlider getIFGainSlider() + { + if(mIFGainSlider == null) + { + mIFGainSlider = new JSlider(JSlider.HORIZONTAL, AirspyTunerController.IF_GAIN_MIN, + AirspyTunerController.IF_GAIN_MAX, AirspyTunerController.IF_GAIN_MIN); + mIFGainSlider.setEnabled(false); + mIFGainSlider.setMajorTickSpacing(1); + mIFGainSlider.setPaintTicks(true); + mIFGainSlider.addChangeListener(event -> + { + int gain = mIFGainSlider.getValue(); + + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getController().setIFGain(gain); + save(); + } + catch(Exception e) + { + mLog.error("Couldn't set airspy IF gain to:" + gain, e); + JOptionPane.showMessageDialog(mIFGainSlider, "Couldn't set IF gain value to " + gain); + } + } + + getIFGainValueLabel().setText(String.valueOf(gain)); + }); + } + + return mIFGainSlider; + } + + private JLabel getMasterGainLabel() + { + if(mMasterGainLabel == null) + { + mMasterGainLabel = new JLabel("Master:"); + } + + return mMasterGainLabel; + } + + private JLabel getMasterGainValueLabel() + { + if(mMasterGainValueLabel == null) + { + mMasterGainValueLabel = new JLabel("0"); + mMasterGainValueLabel.setEnabled(false); + } + + return mMasterGainValueLabel; + } + + private JSlider getMasterGainSlider() + { + if(mMasterGainSlider == null) + { + mMasterGainSlider = new JSlider(JSlider.HORIZONTAL, AirspyTunerController.GAIN_MIN, + AirspyTunerController.GAIN_MAX, AirspyTunerController.GAIN_MIN); + mMasterGainSlider.setEnabled(false); + mMasterGainSlider.setMajorTickSpacing(1); + mMasterGainSlider.setPaintTicks(true); + + mMasterGainSlider.addChangeListener(event -> { GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); - int value = mMasterGain.getValue(); + int value = mMasterGainSlider.getValue(); Gain gain = Gain.getGain(mode, value); - try + if(hasTuner() && !isLoading()) { - mController.setGain(gain); - save(); - mMasterGainValueLabel.setText(String.valueOf(gain.getValue())); + try + { + getTuner().getController().setGain(gain); + save(); + } + catch(Exception e) + { + mLog.error("Couldn't set airspy gain to:" + gain.name(), e); + JOptionPane.showMessageDialog(mMasterGainSlider, "Couldn't set gain value to " + + gain.getValue()); + } } - catch(Exception e) - { - mLog.error("Couldn't set airspy gain to:" + gain.name(), e); - JOptionPane.showMessageDialog(mMasterGain, "Couldn't set gain value to " + - gain.getValue()); - } - } - }); - add(mMasterGain, "span 2"); + getMasterGainValueLabel().setText(String.valueOf(value)); + }); + } - mMasterGainValueLabel = new JLabel("0"); - add(mMasterGainValueLabel); + return mMasterGainSlider; + } - add(new JLabel("IF:")); - - mIFGain = new JSlider(JSlider.HORIZONTAL, - AirspyTunerController.IF_GAIN_MIN, - AirspyTunerController.IF_GAIN_MAX, - AirspyTunerController.IF_GAIN_MIN); - - mIFGain.setMajorTickSpacing(1); - mIFGain.setPaintTicks(true); - - mIFGain.addChangeListener(new ChangeListener() + private JComboBox getGainModeCombo() + { + if(mGainModeCombo == null) { - @Override - public void stateChanged(ChangeEvent event) + mGainModeCombo = new JComboBox<>(GainMode.values()); + mGainModeCombo.setEnabled(false); + mGainModeCombo.addActionListener(e -> { - int gain = mIFGain.getValue(); - - try + if(hasTuner() && !isLoading()) { - mController.setIFGain(gain); + GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); + int value = getMasterGainSlider().getValue(); + Gain gain = Gain.getGain(mode, value); + updateGainComponents(gain); save(); - mIFGainValueLabel.setText(String.valueOf(gain)); } - catch(Exception e) - { - mLog.error("Couldn't set airspy IF gain to:" + gain, e); + }); + } - JOptionPane.showMessageDialog(mIFGain, "Couldn't set IF gain value to " + gain); - } - } - }); + return mGainModeCombo; + } - add(mIFGain, "span 2"); - - mIFGainValueLabel = new JLabel("0"); - add(mIFGainValueLabel); - - /** - * Mixer Gain - */ - add(new JLabel("Mixer:")); - - mMixerGain = new JSlider(JSlider.HORIZONTAL, - AirspyTunerController.MIXER_GAIN_MIN, - AirspyTunerController.MIXER_GAIN_MAX, - AirspyTunerController.MIXER_GAIN_MIN); - - mMixerGain.setMajorTickSpacing(1); - mMixerGain.setPaintTicks(true); - - mMixerGain.addChangeListener(new ChangeListener() + private JComboBox getSampleRateCombo() + { + if(mSampleRateCombo == null) { - @Override - public void stateChanged(ChangeEvent event) + mSampleRateCombo = new JComboBox<>(); + mSampleRateCombo.setEnabled(false); + mSampleRateCombo.addActionListener(new ActionListener() { - int gain = mMixerGain.getValue(); - - try + @Override + public void actionPerformed(ActionEvent e) { - mController.setMixerGain(gain); - save(); - mMixerGainValueLabel.setText(String.valueOf(gain)); + if(hasTuner() && !isLoading()) + { + AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem(); + + try + { + getTuner().getController().setSampleRate(rate); + save(); + } + catch(Exception e1) + { + JOptionPane.showMessageDialog(AirspyTunerEditor.this, + "Couldn't set sample rate to " + rate.getLabel()); + mLog.error("Error setting airspy sample rate", e1); + } + } } - catch(Exception e) - { - mLog.error("Couldn't set airspy Mixer gain to:" + gain, e); - JOptionPane.showMessageDialog(mIFGain, "Couldn't set Mixer gain value to " + gain); - } - } - }); + }); + } - add(mMixerGain, "span 2"); + return mSampleRateCombo; + } - mMixerGainValueLabel = new JLabel("0"); - add(mMixerGainValueLabel); - - add(new JLabel("LNA:")); - - mLNAGain = new JSlider(JSlider.HORIZONTAL, - AirspyTunerController.LNA_GAIN_MIN, - AirspyTunerController.LNA_GAIN_MAX, - AirspyTunerController.LNA_GAIN_MIN); - - mLNAGain.setMajorTickSpacing(1); - mLNAGain.setPaintTicks(true); - - mLNAGain.addChangeListener(new ChangeListener() + /** + * Hyperlink button that provides tuner information + */ + private JButton getTunerInfoButton() + { + if(mTunerInfoButton == null) { - @Override - public void stateChanged(ChangeEvent event) - { - int gain = mLNAGain.getValue(); + mTunerInfoButton = new JButton("Info"); + mTunerInfoButton.setEnabled(false); + mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(AirspyTunerEditor.this, + getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE)); + } - try - { - mController.setLNAGain(gain); - save(); - mLNAGainValueLabel.setText(String.valueOf(gain)); - } - catch(Exception e) - { - mLog.error("Couldn't set airspy LNA gain to:" + gain, e); - JOptionPane.showMessageDialog(mIFGain, "Couldn't set LNA gain value to " + gain); - } - } - }); - - add(mLNAGain, "span 2"); - - mLNAGainValueLabel = new JLabel("0"); - add(mLNAGainValueLabel); - - mMixerAGC = new JCheckBox("Mixer AGC"); - mMixerAGC.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - try - { - mController.setMixerAGC(mMixerAGC.isSelected()); - mMixerGain.setEnabled(!mMixerAGC.isSelected()); - - save(); - } - catch(Exception e1) - { - mLog.error("Error setting Mixer AGC Enabled"); - } - } - }); - - add(mMixerAGC, "span 2,center"); - - mLNAAGC = new JCheckBox("LNA AGC"); - mLNAAGC.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - try - { - mController.setLNAAGC(mLNAAGC.isSelected()); - mLNAGain.setEnabled(!mLNAAGC.isSelected()); - - save(); - } - catch(Exception e1) - { - mLog.error("Error setting LNA AGC Enabled"); - } - - } - }); - - add(mLNAAGC, "span 2,center"); + return mTunerInfoButton; } /** @@ -453,127 +506,112 @@ public class AirspyTunerEditor extends TunerConfigurationEditor * and sensitivity and the individual gain controls are disabled, and * vice-versa for custom mode. */ - private void updateGainComponents(GainMode mode) + private void updateGainComponents(Gain gain) { - switch(mode) + if(hasTuner()) { - case LINEARITY: - if(!mMasterGain.isEnabled()) - { - mMasterGain.setEnabled(true); - mMasterGainValueLabel.setEnabled(true); - } - if(mIFGain.isEnabled()) - { - mIFGain.setEnabled(false); - mIFGainValueLabel.setEnabled(false); - } - if(mLNAGain.isEnabled()) - { - mLNAAGC.setEnabled(false); - mLNAGainValueLabel.setEnabled(false); - mLNAGain.setEnabled(false); - } - if(mMixerGain.isEnabled()) - { - mMixerGainValueLabel.setEnabled(false); - mMixerAGC.setEnabled(false); - mMixerGain.setEnabled(false); - } - break; - case SENSITIVITY: - if(!mMasterGain.isEnabled()) - { - mMasterGain.setEnabled(true); - mMasterGainValueLabel.setEnabled(true); - } - if(mIFGain.isEnabled()) - { - mIFGain.setEnabled(false); - mIFGainValueLabel.setEnabled(false); - } - if(mLNAGain.isEnabled()) - { - mLNAAGC.setEnabled(false); - mLNAGainValueLabel.setEnabled(false); - mLNAGain.setEnabled(false); - } - if(mMixerGain.isEnabled()) - { - mMixerGainValueLabel.setEnabled(false); - mMixerAGC.setEnabled(false); - mMixerGain.setEnabled(false); - } - break; - case CUSTOM: - if(mMasterGain.isEnabled()) - { - mMasterGain.setEnabled(false); - mMasterGainValueLabel.setEnabled(false); - } - if(!mIFGain.isEnabled()) - { - mIFGain.setEnabled(true); - mIFGainValueLabel.setEnabled(true); - } - if(!mLNAGain.isEnabled()) - { - mLNAAGC.setEnabled(true); - mLNAGainValueLabel.setEnabled(true); - mLNAGain.setEnabled(true); - } - if(!mMixerGain.isEnabled()) - { - mMixerGainValueLabel.setEnabled(true); - mMixerAGC.setEnabled(true); - mMixerGain.setEnabled(true); - } - break; - default: - break; + boolean isCustom = gain == Gain.CUSTOM; + + getGainModeCombo().setEnabled(true); + getGainModeCombo().setSelectedItem(gain.getGainMode()); + getMasterGainLabel().setEnabled(!isCustom); + getMasterGainSlider().setEnabled(!isCustom); + getMasterGainSlider().setValue(gain.getValue()); + getMasterGainValueLabel().setEnabled(!isCustom); + getIFGainLabel().setEnabled(isCustom); + getIFGainSlider().setEnabled(isCustom); + getIFGainValueLabel().setEnabled(isCustom); + getLNAAGCCheckBox().setEnabled(isCustom); + getLNAGainSlider().setEnabled(isCustom && !getConfiguration().isLNAAGC()); + getLNAGainValueLabel().setEnabled(isCustom); + getMixerAGCCheckBox().setEnabled(isCustom); + getMixerGainSlider().setEnabled(isCustom && !getConfiguration().isMixerAGC()); + getMixerGainValueLabel().setEnabled(isCustom); + if(isCustom) + { + getIFGainSlider().setValue(getConfiguration().getIFGain()); + getLNAGainSlider().setValue(getConfiguration().getLNAGain()); + getMixerGainSlider().setValue(getConfiguration().getMixerGain()); + getMixerAGCCheckBox().setSelected(getConfiguration().isMixerAGC()); + getLNAAGCCheckBox().setSelected(getConfiguration().isLNAAGC()); + } + else + { + getIFGainSlider().setValue(0); + getLNAGainSlider().setValue(0); + getMixerGainSlider().setValue(0); + getMixerAGCCheckBox().setSelected(false); + getLNAAGCCheckBox().setSelected(false); + } + } + else + { + getGainModeCombo().setEnabled(false); + getMasterGainLabel().setEnabled(false); + getMasterGainSlider().setEnabled(false); + getMasterGainSlider().setValue(0); + getMasterGainValueLabel().setEnabled(false); + getIFGainLabel().setEnabled(false); + getIFGainSlider().setEnabled(false); + getIFGainSlider().setValue(0); + getIFGainValueLabel().setEnabled(false); + getLNAAGCCheckBox().setEnabled(false); + getLNAAGCCheckBox().setSelected(false); + getLNAGainSlider().setEnabled(false); + getLNAGainSlider().setValue(0); + getLNAGainValueLabel().setEnabled(false); + getMixerAGCCheckBox().setEnabled(false); + getMixerAGCCheckBox().setSelected(false); + getMixerGainSlider().setEnabled(false); + getMixerGainSlider().setValue(0); + getMixerGainValueLabel().setEnabled(false); } } @Override public void save() { - if(hasItem() && !mLoading) + if(hasConfiguration() && !isLoading()) { - AirspyTunerConfiguration config = getConfiguration(); - config.setName(mConfigurationName.getText()); - config.setSampleRate(((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate()); - - double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection(value); - - Gain gain = Gain.getGain((GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue()); - - config.setGain(gain); - config.setIFGain(mIFGain.getValue()); - config.setMixerGain(mMixerGain.getValue()); - config.setLNAGain(mLNAGain.getValue()); - config.setMixerAGC(mMixerAGC.isSelected()); - config.setLNAAGC(mLNAAGC.isSelected()); - - getTunerConfigurationModel().broadcast(new TunerConfigurationEvent(config, TunerConfigurationEvent.Event.CHANGE)); + getConfiguration().setFrequency(getFrequencyControl().getFrequency()); + double value = ((SpinnerNumberModel) getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); + getConfiguration().setFrequencyCorrection(value); + getConfiguration().setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected()); + getConfiguration().setSampleRate(((AirspySampleRate)getSampleRateCombo().getSelectedItem()).getRate()); + Gain gain = Gain.getGain((GainMode)mGainModeCombo.getSelectedItem(), getMasterGainSlider().getValue()); + getConfiguration().setGain(gain); + getConfiguration().setIFGain(getIFGainSlider().getValue()); + getConfiguration().setMixerGain(getMixerGainSlider().getValue()); + getConfiguration().setLNAGain(getLNAGainSlider().getValue()); + getConfiguration().setMixerAGC(getMixerAGCCheckBox().isSelected()); + getConfiguration().setLNAAGC(getLNAAGCCheckBox().isSelected()); + saveConfiguration(); } } - private AirspySampleRate fromValue(int value) + /** + * Finds the airspy sample rate entry that matches the value. + * @param value in Hertz + * @return the matching rate entry or null. + */ + private AirspySampleRate getSampleRate(int value) { - List rates = mController.getSampleRates(); - - for(AirspySampleRate rate : rates) + if(hasTuner()) { - if(rate.getRate() == value) + List rates = getTuner().getController().getSampleRates(); + + for(AirspySampleRate rate : rates) { - return rate; + if(rate.getRate() == value) + { + return rate; + } } - } - if(rates.size() > 0) - { - return rates.get(0); + if(rates.size() > 0) + { + return rates.get(0); + } } return null; @@ -584,7 +622,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor */ private void updateSampleRateToolTip() { - if(mController.isLocked()) + if(hasTuner() && getTuner().getController().isLocked()) { mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); } @@ -597,207 +635,44 @@ public class AirspyTunerEditor extends TunerConfigurationEditor @Override public void setTunerLockState(boolean locked) { - mSampleRateCombo.setEnabled(!locked); - } - - /** - * Sets all controls to the argument enabled state - */ - private void setControlsEnabled(boolean enabled) - { - if(mConfigurationName.isEnabled() != enabled) - { - mConfigurationName.setEnabled(enabled); - } - - if(mTunerInfo.isEnabled() != enabled) - { - mTunerInfo.setEnabled(enabled); - } - - updateSampleRateToolTip(); - - if(mController.isLocked()) - { - mSampleRateCombo.setEnabled(false); - } - else if(mSampleRateCombo.isEnabled() != enabled) - { - mSampleRateCombo.setEnabled(enabled); - } - - if(mFrequencyCorrection.isEnabled() != enabled) - { - mFrequencyCorrection.setEnabled(enabled); - } - - if(mGainModeCombo.isEnabled() != enabled) - { - mGainModeCombo.setEnabled(enabled); - } - - if(mMasterGain.isEnabled() != enabled) - { - mMasterGain.setEnabled(enabled); - } - - if(mIFGain.isEnabled() != enabled) - { - mIFGain.setEnabled(enabled); - } - - if(mMixerGain.isEnabled() != enabled) - { - mMixerGain.setEnabled(enabled); - } - - if(mMixerAGC.isEnabled() != enabled) - { - mMixerAGC.setEnabled(enabled); - } - - if(mLNAGain.isEnabled() != enabled) - { - mLNAGain.setEnabled(enabled); - } - - if(mLNAAGC.isEnabled() != enabled) - { - mLNAAGC.setEnabled(enabled); - } - } - - /** - * Applies the tuner configuration to the editor. If the configuration is assigned to the - * currently selected tuner, then the settings are applied. - */ - @Override - public void setItem(TunerConfiguration config) - { - super.setItem(config); - - mLoading = true; - - if(hasItem() && getItem().isAssigned()) - { - AirspyTunerConfiguration airspy = getConfiguration(); - - setControlsEnabled(true); - - mConfigurationName.setText(airspy.getName()); - - AirspySampleRate rate = fromValue(airspy.getSampleRate()); - - if(rate != null) - { - mSampleRateCombo.setSelectedItem(rate); - } - - //Update enabled state to reflect when frequency and sample rate controls are locked - mSampleRateCombo.setEnabled(!mController.isLocked()); - - mFrequencyCorrection.removeChangeListener(mFrequencyCorrectionChangeListener); - mFrequencyCorrection.setValue(airspy.getFrequencyCorrection()); - mFrequencyCorrection.addChangeListener(mFrequencyCorrectionChangeListener); - - GainMode mode = Gain.getGainMode(airspy.getGain()); - - //This will set the master, IF, Mixer, LNA and AGC controls - mGainModeCombo.setSelectedItem(mode); - - if(mode == GainMode.CUSTOM) - { - mMasterGain.setValue(airspy.getGain().getValue()); - mIFGain.setValue(airspy.getIFGain()); - - if(airspy.isMixerAGC()) - { - mMixerAGC.setSelected(airspy.isMixerAGC()); - mMixerGain.setEnabled(false); - } - else - { - mMixerGain.setValue(airspy.getMixerGain()); - } - - if(airspy.isLNAAGC()) - { - mLNAAGC.setSelected(airspy.isLNAAGC()); - mLNAGain.setEnabled(false); - } - else - { - mLNAGain.setValue(airspy.getLNAGain()); - } - } - else - { - mMixerAGC.setSelected(false); - mLNAAGC.setSelected(false); - - Gain gain = airspy.getGain(); - - mMasterGain.setValue(gain.getValue()); - mIFGain.setValue(gain.getIF()); - mMixerGain.setValue(gain.getMixer()); - mLNAGain.setValue(gain.getLNA()); - } - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(hasItem() ? getItem().getName() : ""); - } - - mLoading = false; + super.setTunerLockState(locked); + getSampleRateCombo().setEnabled(!locked); } private String getTunerInfo() { - StringBuilder sb = new StringBuilder(); - - AirspyDeviceInformation info = mController.getDeviceInfo(); - - sb.append("

Airspy Tuner

"); - sb.append("Serial: "); - sb.append(info.getSerialNumber()); - sb.append("
"); - - sb.append("Firmware: "); - String[] firmware = info.getVersion().split(" "); - sb.append(firmware.length > 1 ? firmware[0] : info.getVersion()); - sb.append("
"); - - sb.append("Part: "); - sb.append(info.getPartNumber()); - sb.append("
"); - - sb.append("Board ID: "); - sb.append(info.getBoardID().getLabel()); - sb.append("
"); - - return sb.toString(); - } - - private class FrequencyCorrectionChangeListener implements ChangeListener - { - - @Override - public void stateChanged(ChangeEvent e) + if(getDiscoveredTuner().getTunerStatus() == TunerStatus.ERROR) { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection(value); - } - catch(SourceException e1) - { - mLog.error("Error setting frequency correction value", e1); - } - - save(); + return getDiscoveredTuner().getErrorMessage(); } + + if(hasTuner()) + { + StringBuilder sb = new StringBuilder(); + + AirspyDeviceInformation info = getTuner().getController().getDeviceInfo(); + + sb.append("

Airspy Tuner

"); + sb.append("Serial: "); + sb.append(info.getSerialNumber()); + sb.append("
"); + + sb.append("Firmware: "); + String[] firmware = info.getVersion().split(" "); + sb.append(firmware.length > 1 ? firmware[0] : info.getVersion()); + sb.append("
"); + + sb.append("Part: "); + sb.append(info.getPartNumber()); + sb.append("
"); + + sb.append("Board ID: "); + sb.append(info.getBoardID().getLabel()); + sb.append("
"); + + return sb.toString(); + } + + return null; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/MultiFrequencyTunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/MultiFrequencyTunerChannelSource.java index bf84742d..1fbf2b07 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/channel/MultiFrequencyTunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/MultiFrequencyTunerChannelSource.java @@ -26,8 +26,8 @@ import io.github.dsheirer.source.Source; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.heartbeat.Heartbeat; -import io.github.dsheirer.source.tuner.TunerModel; import io.github.dsheirer.source.tuner.channel.rotation.FrequencyLockChangeRequest; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +46,7 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource { private final static Logger mLog = LoggerFactory.getLogger(MultiFrequencyTunerChannelSource.class); - private TunerModel mTunerModel; + private TunerManager mTunerManager; private TunerChannelSource mTunerChannelSource; private List mFrequencies; private List mLockedFrequencies = new ArrayList<>(); @@ -59,12 +59,12 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource private boolean mStarted; private ConsumerSourceEventAdapter mConsumerSourceEventAdapter = new ConsumerSourceEventAdapter(); - public MultiFrequencyTunerChannelSource(TunerModel tunerModel, TunerChannelSource tunerChannelSource, + public MultiFrequencyTunerChannelSource(TunerManager tunerManager, TunerChannelSource tunerChannelSource, List frequencies, ChannelSpecification channelSpecification, String preferredTuner) { super(null, tunerChannelSource.getTunerChannel()); - mTunerModel = tunerModel; + mTunerManager = tunerManager; mTunerChannelSource = tunerChannelSource; mTunerChannelSource.setSourceEventListener(mConsumerSourceEventAdapter); mFrequencies = frequencies; @@ -115,7 +115,7 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource { if(mStarted) { - Source source = mTunerModel.getSource(nextChannel, mChannelSpecification, mPreferredTuner); + Source source = mTunerManager.getSource(nextChannel, mChannelSpecification, mPreferredTuner); if(source instanceof TunerChannelSource) { diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java index 0f121b61..7a866b14 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java @@ -172,6 +172,10 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc sourceEvent.setSource(this); broadcastProducerSourceEvent(sourceEvent); break; + case NOTIFICATION_TUNER_SHUTDOWN: + //Rebroadcast this event so the channel processing manager receives it and shuts down the channel + broadcastConsumerSourceEvent(sourceEvent); + break; //Request events from the consumer case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE: setChannelFrequencyCorrection(sourceEvent.getValue().longValue()); diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/DisabledTuner.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/DisabledTuner.java new file mode 100644 index 00000000..e03fa6a8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/DisabledTuner.java @@ -0,0 +1,41 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.configuration; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; + +/** + * Record to track disabled USB tuners + */ +@JsonSerialize +public record DisabledTuner(TunerClass tunerClass, String id) +{ + /** + * Indicates if this disabled tuner matches the discovered tuner argument + * @param other discovered tuner to compare + * @return true if they match + */ + public boolean matches(DiscoveredTuner other) + { + return this.tunerClass().equals(other.getTunerClass()) && this.id().equals(other.getId()); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfiguration.java index 04551aee..1b8c5568 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfiguration.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.configuration; @@ -36,56 +33,50 @@ import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration; import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration; /** - * Abstract class to hold a named configuration for a specific type of tuner + * Abstract class to hold a configuration for a specific type of tuner */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.PROPERTY, property="type") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ - @JsonSubTypes.Type(value=AirspyTunerConfiguration.class, name="airspyTunerConfiguration"), - @JsonSubTypes.Type(value=E4KTunerConfiguration.class, name="e4KTunerConfiguration"), - @JsonSubTypes.Type(value=FCD1TunerConfiguration.class, name="fcd1TunerConfiguration"), - @JsonSubTypes.Type(value=FCD2TunerConfiguration.class, name="fcd2TunerConfiguration"), - @JsonSubTypes.Type(value=HackRFTunerConfiguration.class, name="hackRFTunerConfiguration"), - @JsonSubTypes.Type(value= RecordingTunerConfiguration.class, name="recordingTunerConfiguration"), - @JsonSubTypes.Type(value=R820TTunerConfiguration.class, name="r820TTunerConfiguration"), + @JsonSubTypes.Type(value = AirspyTunerConfiguration.class, name = "airspyTunerConfiguration"), + @JsonSubTypes.Type(value = E4KTunerConfiguration.class, name = "e4KTunerConfiguration"), + @JsonSubTypes.Type(value = FCD1TunerConfiguration.class, name = "fcd1TunerConfiguration"), + @JsonSubTypes.Type(value = FCD2TunerConfiguration.class, name = "fcd2TunerConfiguration"), + @JsonSubTypes.Type(value = HackRFTunerConfiguration.class, name = "hackRFTunerConfiguration"), + @JsonSubTypes.Type(value = RecordingTunerConfiguration.class, name = "recordingTunerConfiguration"), + @JsonSubTypes.Type(value = R820TTunerConfiguration.class, name = "r820TTunerConfiguration"), }) -@JacksonXmlRootElement( localName = "tuner_configuration" ) +@JacksonXmlRootElement(localName = "tuner_configuration") public abstract class TunerConfiguration { - protected String mName; - protected String mUniqueID; - protected boolean mAssigned; - protected long mFrequency; + private String mUniqueID; + private long mFrequency = 101100000; + private double mFrequencyCorrection = 0.0d; + private boolean mAutoPPMCorrection = true; /** - * Default constructor to support JAXB + * Default constructor to support Jackson */ public TunerConfiguration() { } + /** + * Identifies the tuner type for this configuration + */ + @JsonIgnore + public abstract TunerType getTunerType(); + /** * Normal constructor */ - public TunerConfiguration(String uniqueID, String name) + public TunerConfiguration(String uniqueID) { mUniqueID = uniqueID; - mName = name; } public String toString() { - return mName; - } - - @JacksonXmlProperty(isAttribute = true, localName = "name") - public String getName() - { - return mName; - } - - public void setName(String name) - { - mName = name; + return getTunerType() + " " + getUniqueID(); } @JacksonXmlProperty(isAttribute = true, localName = "unique_id") @@ -97,18 +88,6 @@ public abstract class TunerConfiguration public void setUniqueID(String id) { mUniqueID = id; - ; - } - - @JacksonXmlProperty(isAttribute = true, localName = "assigned") - public boolean isAssigned() - { - return mAssigned; - } - - public void setAssigned(boolean assigned) - { - mAssigned = assigned; } @JacksonXmlProperty(isAttribute = true, localName = "frequency") @@ -122,12 +101,35 @@ public abstract class TunerConfiguration mFrequency = frequency; } - @JsonIgnore - public abstract TunerType getTunerType(); + @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") + public double getFrequencyCorrection() + { + return mFrequencyCorrection; + } - //Below is maintained for compatibility with javax.xml.bind serialization of the settings file. - //This can be removed if/once there is a new version of the settings. - @JacksonXmlProperty(isAttribute = true, localName = "tuner_type") - public String getLegacyAttribute(){ return null; } - public void setLegacyAttribute(String bonkers) { } + public void setFrequencyCorrection(double value) + { + mFrequencyCorrection = value; + } + + /** + * Indicates if automatic correction of PPM from measured frequency error is enabled/disabled. + * + * @return true if auto-correction is enabled. + */ + @JacksonXmlProperty(isAttribute = true, localName = "auto_ppm_correction_enabled") + public boolean getAutoPPMCorrectionEnabled() + { + return mAutoPPMCorrection; + } + + /** + * Sets the enabled state for auto-correction of PPM from measured frequency error values. + * + * @param enabled + */ + public void setAutoPPMCorrectionEnabled(boolean enabled) + { + mAutoPPMCorrection = enabled; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java deleted file mode 100644 index 34d96a7d..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.source.tuner.configuration; - -import io.github.dsheirer.gui.editor.Editor; - -public abstract class TunerConfigurationEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - private TunerConfigurationModel mTunerConfigurationModel; - - public TunerConfigurationEditor(TunerConfigurationModel model) - { - mTunerConfigurationModel = model; - } - - public TunerConfigurationModel getTunerConfigurationModel() - { - return mTunerConfigurationModel; - } - - /** - * Sets the lock state for the tuner so that the frequency and sample rate controls can be - * enabled/disabled. - * - * @param locked true if the tuner is locked. - */ - public abstract void setTunerLockState(boolean locked); -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationFactory.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationFactory.java index 92f6763a..4939f607 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationFactory.java @@ -1,105 +1,112 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.configuration; -import io.github.dsheirer.gui.editor.Editor; -import io.github.dsheirer.source.tuner.Tuner; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.airspy.AirspyTuner; import io.github.dsheirer.source.tuner.airspy.AirspyTunerConfiguration; import io.github.dsheirer.source.tuner.airspy.AirspyTunerEditor; -import io.github.dsheirer.source.tuner.fcd.FCDTuner; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerConfiguration; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerEditor; import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerConfiguration; import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerEditor; -import io.github.dsheirer.source.tuner.hackrf.HackRFTuner; import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration; import io.github.dsheirer.source.tuner.hackrf.HackRFTunerEditor; -import io.github.dsheirer.source.tuner.recording.RecordingTuner; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration; -import io.github.dsheirer.source.tuner.recording.RecordingTunerConfigurationEditor; -import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; +import io.github.dsheirer.source.tuner.recording.RecordingTunerEditor; +import io.github.dsheirer.source.tuner.rtl.RTL2832UnknownTunerEditor; import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration; import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerEditor; import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration; import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerEditor; +import io.github.dsheirer.source.tuner.ui.TunerEditor; +/** + * Factory methods to support tuner configurations and configuration editors. + */ public class TunerConfigurationFactory { /** * Creates a tuner configuration for the specified tuner type, unique ID and name */ - public static TunerConfiguration getTunerConfiguration(TunerType type, String uniqueID, - String name) + public static TunerConfiguration getTunerConfiguration(TunerType type, String uniqueID) { switch(type) { case AIRSPY_R820T: - return new AirspyTunerConfiguration(uniqueID, name); + return new AirspyTunerConfiguration(uniqueID); case ELONICS_E4000: - return new E4KTunerConfiguration(uniqueID, name); + return new E4KTunerConfiguration(uniqueID); case FUNCUBE_DONGLE_PRO: - return new FCD1TunerConfiguration(uniqueID, name); + return new FCD1TunerConfiguration(uniqueID); case FUNCUBE_DONGLE_PRO_PLUS: - return new FCD2TunerConfiguration(uniqueID, name); - case HACKRF: - return new HackRFTunerConfiguration(uniqueID, name); + return new FCD2TunerConfiguration(uniqueID); + case HACKRF_JAWBREAKER: + case HACKRF_ONE: + case HACKRF_RAD1O: + return new HackRFTunerConfiguration(uniqueID); case RAFAELMICRO_R820T: - return new R820TTunerConfiguration(uniqueID, name); + return new R820TTunerConfiguration(uniqueID); case RECORDING: - return new RecordingTunerConfiguration(uniqueID, name); + return new RecordingTunerConfiguration(uniqueID); default: - throw new IllegalArgumentException("Unrecognized tuner type [" - + type.name() + "] - can't create named [" + name + "] tuner" - + " configuration"); + throw new IllegalArgumentException("Unrecognized tuner type [" + type.name() + "]"); } } /** * Creates a tuner editor gui for the specified tuner */ - public static Editor getEditor(Tuner tuner, TunerConfigurationModel model) + public static TunerEditor getEditor(UserPreferences userPreferences, DiscoveredTuner discoveredTuner, + TunerManager tunerManager) { - switch(tuner.getTunerType()) + switch(discoveredTuner.getTunerClass()) { - case AIRSPY_R820T: - return new AirspyTunerEditor(model, (AirspyTuner)tuner); - case ELONICS_E4000: - return new E4KTunerEditor(model, (RTL2832Tuner)tuner); + case AIRSPY: + return new AirspyTunerEditor(userPreferences, tunerManager, discoveredTuner); case FUNCUBE_DONGLE_PRO: - return new FCD1TunerEditor(model, (FCDTuner)tuner); + return new FCD1TunerEditor(userPreferences, tunerManager, discoveredTuner); case FUNCUBE_DONGLE_PRO_PLUS: - return new FCD2TunerEditor(model, (FCDTuner)tuner); + return new FCD2TunerEditor(userPreferences, tunerManager, discoveredTuner); case HACKRF: - return new HackRFTunerEditor(model, (HackRFTuner)tuner); - case RAFAELMICRO_R820T: - return new R820TTunerEditor(model, (RTL2832Tuner)tuner); - case RECORDING: - return new RecordingTunerConfigurationEditor(model, (RecordingTuner)tuner); + return new HackRFTunerEditor(userPreferences, tunerManager, discoveredTuner); + case RTL2832: + if(discoveredTuner.hasTuner()) + { + switch(discoveredTuner.getTuner().getTunerType()) + { + case ELONICS_E4000: + return new E4KTunerEditor(userPreferences, tunerManager, discoveredTuner); + case RAFAELMICRO_R820T: + return new R820TTunerEditor(userPreferences, tunerManager, discoveredTuner); + } + } + return new RTL2832UnknownTunerEditor(userPreferences, tunerManager, discoveredTuner); + case RECORDING_TUNER: + return new RecordingTunerEditor(userPreferences, tunerManager, discoveredTuner); + case TEST_TUNER: case UNKNOWN: default: - throw new IllegalArgumentException("Unrecognized Tuner: " + tuner.getName()); + throw new IllegalArgumentException("Unsupported tuner class: " + discoveredTuner.getTunerClass()); } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java new file mode 100644 index 00000000..931b7fab --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java @@ -0,0 +1,315 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.configuration; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.TunerType; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener; +import io.github.dsheirer.source.tuner.manager.TunerStatus; +import io.github.dsheirer.util.ThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Manages settings and configurations for all tuner types. + */ +public class TunerConfigurationManager implements IDiscoveredTunerStatusListener +{ + private static final Logger mLog = LoggerFactory.getLogger(TunerConfigurationManager.class); + private static final String SETTINGS_FILE_NAME = "tuner_configuration.json"; + private UserPreferences mUserPreferences; + private List mDisabledTunerList = new ArrayList<>(); + private List mTunerConfigurations = new ArrayList<>(); + private AtomicBoolean mSavePending = new AtomicBoolean(); + private Lock mLock = new ReentrantLock(); + + /** + * Constructs an instance and loads the save configuration state. + * + * @param userPreferences to determine directories for accessing files + */ + public TunerConfigurationManager(UserPreferences userPreferences) + { + mUserPreferences = userPreferences; + load(); + } + + /** + * Loads settings from the persisted tuner state file + */ + private void load() + { + Path configPath = getConfigurationFilePath(); + + if(Files.exists(configPath)) + { + ObjectMapper objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + try + { + TunerConfigurationState state = objectMapper.readValue(configPath.toFile(), TunerConfigurationState.class); + mDisabledTunerList.addAll(state.getDisabledTuners()); + mTunerConfigurations.addAll(state.getTunerConfigurations()); + } + catch(IOException ioe) + { + mLog.error("Error loading tuner configuration file", ioe); + } + } + } + + /** + * Tuner configuration state file (.json). + */ + private Path getConfigurationFilePath() + { + return mUserPreferences.getDirectoryPreference().getDirectoryConfiguration().resolve(SETTINGS_FILE_NAME); + } + + /** + * Saves the current tuner configuration state to disk. + */ + private void save() + { + TunerConfigurationState state = new TunerConfigurationState(); + + mLock.lock(); + + try + { + state.setDisabledTuners(new ArrayList<>(mDisabledTunerList)); + state.setTunerConfigurations(new ArrayList<>(mTunerConfigurations)); + } + catch(Exception e) + { + mLog.error("Error", e); + } + finally + { + mLock.unlock(); + } + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + + try + { + objectMapper.writeValue(getConfigurationFilePath().toFile(), state); + } + catch(IOException ioe) + { + mLog.error("Error writing tuner configuration state to file [" + getConfigurationFilePath() + "]", ioe); + } + } + + /** + * Schedules a configurations save task. Subsequent calls to this method will be ignored until the + * save event occurs, thus limiting repetitive saving to a minimum. + */ + public void saveConfigurations() + { + if(mSavePending.compareAndSet(false, true)) + { + ThreadPool.SCHEDULED.schedule(new ConfigurationSaveTask(), 2, TimeUnit.SECONDS); + } + } + + @Override + public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current) + { + if(current == TunerStatus.DISABLED) + { + mLog.info("Setting Tuner to Disabled: " + discoveredTuner.getId()); + addDisabledTuner(discoveredTuner); + } + else if(current == TunerStatus.ENABLED) + { + mLog.info("Setting Tuner to Enabled: " + discoveredTuner.getId()); + removeDisabledTuner(discoveredTuner); + } + } + + /** + * Adds the discovered tuner to the list of disabled tuners + */ + private void addDisabledTuner(DiscoveredTuner discoveredTuner) + { + if(!isDisabled(discoveredTuner)) + { + mLock.lock(); + + try + { + mDisabledTunerList.add(new DisabledTuner(discoveredTuner.getTunerClass(), discoveredTuner.getId())); + } + finally + { + mLock.unlock(); + } + + saveConfigurations(); + } + } + + /** + * Removes the tuner from the disabled tuners list + * @param discoveredTuner to remove + */ + private void removeDisabledTuner(DiscoveredTuner discoveredTuner) + { + mLock.lock(); + + try + { + mDisabledTunerList.removeIf(tuner -> tuner.matches(discoveredTuner)); + } + finally + { + mLock.unlock(); + } + + saveConfigurations(); + } + + /** + * Indicates if the discovered tuner is disabled. This method should only be used to determine the disabled state + * of a tuner when it is added to the system for use, such as tuner discovery at application startup, or for + * USB tuner hotplug device add notifications. + * + * @param discoveredTuner to check for disabled status. + * @return true if the tuner is supposed to be disabled. + */ + public boolean isDisabled(DiscoveredTuner discoveredTuner) + { + return findDisabledTuner(discoveredTuner) != null; + } + + /** + * Finds the disabled tuner that matches the discovered tuner. + * @param discoveredTuner to search for + * @return disabled tuner instance or null if the discovered tuner is not currently disabled. + */ + private DisabledTuner findDisabledTuner(DiscoveredTuner discoveredTuner) + { + DisabledTuner found = null; + + mLock.lock(); + + try + { + for(DisabledTuner disabledTuner: mDisabledTunerList) + { + if(disabledTuner.matches(discoveredTuner)) + { + found = disabledTuner; + break; + } + } + } + finally + { + mLock.unlock(); + } + + return found; + } + + /** + * Adds the tuner configuration if one doesn't exist that matches the tuner type and unique id. + * @param tunerConfiguration to add + */ + public void addTunerConfiguration(TunerConfiguration tunerConfiguration) + { + mLog.info("Adding tuner config: " + tunerConfiguration); + if(!mTunerConfigurations.stream().filter(config -> config.getTunerType().equals(tunerConfiguration.getTunerType()) && + config.getUniqueID().equalsIgnoreCase(tunerConfiguration.getUniqueID())).findFirst().isPresent()) + { + mTunerConfigurations.add(tunerConfiguration); + saveConfigurations(); + } + } + + /** + * Removes the tuner configuration from this manager. + * @param tunerConfiguration to remove + */ + public void removeTunerConfiguration(TunerConfiguration tunerConfiguration) + { + mTunerConfigurations.remove(tunerConfiguration); + saveConfigurations(); + } + + /** + * Provides an existing or creates a new tuner configuration for the specified tuner type and unique ID value. + * + * Note: this method is not thread-safe. + */ + public TunerConfiguration getTunerConfiguration(TunerType type, String uniqueID ) + { + Optional optional = mTunerConfigurations.stream().filter(config -> config.getTunerType().equals(type) && + config.getUniqueID().equalsIgnoreCase(uniqueID)).findFirst(); + + if(optional.isPresent()) + { + return optional.get(); + } + + TunerConfiguration config = TunerConfigurationFactory.getTunerConfiguration(type, uniqueID); + addTunerConfiguration(config); + return config; + } + + /** + * Saves the current tuner configuration state and resets the save pending flag + */ + public class ConfigurationSaveTask implements Runnable + { + @Override + public void run() + { + try + { + save(); + } + catch(Exception e) + { + mLog.error("Error saving tuner configurations", e); + } + + mSavePending.set(false); + } + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java deleted file mode 100644 index d12cd470..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java +++ /dev/null @@ -1,378 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ - -package io.github.dsheirer.source.tuner.configuration; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.tuner.Tuner; -import io.github.dsheirer.source.tuner.TunerType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.table.AbstractTableModel; -import java.util.ArrayList; -import java.util.List; - -public class TunerConfigurationModel extends AbstractTableModel -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( TunerConfigurationModel.class ); - - public static final int TUNER_TYPE = 0; - public static final int CONFIGURATION_NAME = 1; - public static final int ASSIGNED = 2; - private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name", "Assigned" }; - - private List> mConfigurationChangeListeners = - new ArrayList<>(); - private List mTunerConfigurations = new ArrayList<>(); - - /** - * Table model for managing tuner configurations. Provides tuner configuration events to - * registered listeners - */ - public TunerConfigurationModel() - { - } - - /** - * Adds the list of tuner configurations to the model - */ - public void addTunerConfigurations( List configs ) - { - for( TunerConfiguration config: configs ) - { - addTunerConfiguration( config ); - } - } - - /** - * Returns of list of tuner configurations from the model - */ - public List getTunerConfigurations() - { - return mTunerConfigurations; - } - - /** - * Adds the tuner configuration to the model firing a table model row add - * event and broadcasting a tuner configuration event. - */ - public void addTunerConfiguration( TunerConfiguration config ) - { - if( !mTunerConfigurations.contains( config ) && - config.getUniqueID() != null && - config.getName() != null ) - { - mTunerConfigurations.add( config ); - - int index = mTunerConfigurations.indexOf( config ); - - fireTableRowsInserted( index, index ); - - broadcast( new TunerConfigurationEvent( config, TunerConfigurationEvent.Event.ADD ) ); - } - } - - /** - * Removes the tuner configuration from the model firing a table model row - * delete event and broadcasting a tuner configuration event. - */ - public void removeTunerConfiguration( TunerConfiguration config ) - { - if( mTunerConfigurations.contains( config ) ) - { - int index = mTunerConfigurations.indexOf( config ); - - mTunerConfigurations.remove( config ); - - fireTableRowsDeleted( index, index ); - - broadcast( new TunerConfigurationEvent( config, TunerConfigurationEvent.Event.REMOVE ) ); - } - } - - /** - * Removes all tuner configurations and assignments from the model and - * fires a table clear event and broadcasts a tuner configuration event for - * each row removed. - */ - public void clear() - { - for( TunerConfiguration config: mTunerConfigurations ) - { - broadcast( new TunerConfigurationEvent( config, TunerConfigurationEvent.Event.REMOVE ) ); - } - - int size = mTunerConfigurations.size(); - - mTunerConfigurations.clear(); - - fireTableRowsDeleted( 0, size - 1 ); - } - - /** - * Registers a tuner configuration event listener - */ - public void addListener( Listener listener ) - { - mConfigurationChangeListeners.add( listener ); - } - - /** - * Removes the tuner configuration event listener - */ - public void removeListener( Listener listener ) - { - mConfigurationChangeListeners.remove( listener ); - } - - /** - * Broadcasts a tuner configuration event - */ - public void broadcast( TunerConfigurationEvent event ) - { - for( Listener listener: mConfigurationChangeListeners ) - { - listener.receive( event ); - } - } - - /** - * Designates the configuration argument as the currently assigned configuration and unassigns - * any other configurations with the same tuner type and unique ID. - */ - public void assignTunerConfiguration( TunerConfiguration configToAssign ) - { - if( configToAssign != null ) - { - List configs = - getTunerConfigurations( configToAssign.getTunerType(), configToAssign.getUniqueID() ); - - for( TunerConfiguration config: configs ) - { - if( config.isAssigned() ) - { - config.setAssigned( false ); - - int index = mTunerConfigurations.indexOf( config ); - fireTableCellUpdated( index, ASSIGNED ); - broadcast( new TunerConfigurationEvent( config, TunerConfigurationEvent.Event.CHANGE ) ); - } - } - - configToAssign.setAssigned( true ); - int index = mTunerConfigurations.indexOf( configToAssign ); - fireTableCellUpdated( index, ASSIGNED ); - broadcast( new TunerConfigurationEvent( configToAssign, TunerConfigurationEvent.Event.CHANGE ) ); - } - } - - /** - * Returns the tuner configuration that is assigned to the tuner with the - * tuner type and uniqueID, or selects the first existing configuration that - * matches the tuner type and unique ID and assigns it, or creates a new - * tuner configuration and assigns it to the tuner. - * - * This method will always return a tuner configuration that is assigned to - * the tuner type and unique ID. - */ - public TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID ) - { - List configs = getTunerConfigurations( type, uniqueID ); - - for( TunerConfiguration config: configs ) - { - if( config.isAssigned() && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( uniqueID ) ) - { - return config; - } - } - - //Reuse an existing configuration to assign - if( !configs.isEmpty() ) - { - configs.get( 0 ).setAssigned( true ); - return configs.get( 0 ); - } - - String name = getDistinctName( type, uniqueID ); - - //We didn't find the config so create a new one and assign it - TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration( type, uniqueID, name ); - - config.setAssigned( true ); - - addTunerConfiguration( config ); - - return config; - } - - /** - * Indicates if a configuration exists in this model matching the tuner type - * and unique ID and name. - */ - public boolean hasTunerConfiguration( TunerType type, String uniqueID, String name ) - { - for( TunerConfiguration config: mTunerConfigurations ) - { - if( config.getTunerType() == type && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( uniqueID ) && - config.getName() != null && - config.getName().contentEquals( name ) ) - { - return true; - } - } - - return false; - } - - /** - * Creates a configuration name for the tuner configuration that is distinct - * among the existing tuner configurations of the same type and uniqueID - */ - public String getDistinctName( TunerType type, String uniqueID ) - { - int counter = 0; - - String name = "New"; - - while( hasTunerConfiguration( type, uniqueID, name ) ) - { - counter++; - - name = "New (" + counter + ")"; - } - - return name; - } - - /** - * Returns all tuner configurations for the specified tuner type - */ - public List getTunerConfigurations( TunerType type, String uniqueID ) - { - List configs = new ArrayList<>(); - - for( TunerConfiguration config: mTunerConfigurations ) - { - if( config.getTunerType() == type && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( uniqueID ) ) - { - configs.add( config ); - } - } - - return configs; - } - - /** - * Column names for the table model - */ - @Override - public String getColumnName( int column ) - { - return COLUMN_HEADERS[ column ]; - } - - /** - * Tuner configuration model row count - */ - @Override - public int getRowCount() - { - return mTunerConfigurations.size(); - } - - /** - * Tuner configuration model column count - */ - @Override - public int getColumnCount() - { - return COLUMN_HEADERS.length; - } - - /** - * Returns the row index for the specified configuration or -1 - */ - public int getRowIndex( TunerConfiguration configuration ) - { - if( mTunerConfigurations.contains( configuration ) ) - { - return mTunerConfigurations.indexOf( configuration ); - } - - return -1; - } - - /** - * Returns the configuration at the specified index or null - */ - public TunerConfiguration getTunerConfiguration( int index ) - { - if( index >= 0 && index < mTunerConfigurations.size() ) - { - return mTunerConfigurations.get( index ); - } - - return null; - } - - /** - * Returns the value at the indicated table cell - */ - @Override - public Object getValueAt( int row, int column ) - { - if( row >= 0 && row < mTunerConfigurations.size() ) - { - TunerConfiguration config = mTunerConfigurations.get( row ); - - switch( column ) - { - case TUNER_TYPE: - return config.getTunerType().getLabel(); - case CONFIGURATION_NAME: - return config.getName(); - case ASSIGNED: - return config.isAssigned() ? "*" : null; - default: - break; - } - } - - return null; - } - - /** - * Stores the latest tuned frequency for the tuner in the currently assigned configuration - */ - public void tunerFrequencyChanged( Tuner tuner ) - { - TunerConfiguration assigned = getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - - assigned.setFrequency( tuner.getTunerController().getFrequency() ); - - broadcast( new TunerConfigurationEvent( assigned, TunerConfigurationEvent.Event.CHANGE ) ); - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationState.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationState.java new file mode 100644 index 00000000..c4f6cb08 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationState.java @@ -0,0 +1,71 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.configuration; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.util.ArrayList; +import java.util.List; + +/** + * Persistable instance of tuner configurations and disabled tuners + */ +@JsonSerialize +public class TunerConfigurationState +{ + private List mDisabledTuners = new ArrayList(); + private List mTunerConfigurations = new ArrayList<>(); + + public TunerConfigurationState() + { + } + + /** + * List of currently disabled tuners + */ + public List getDisabledTuners() + { + return mDisabledTuners; + } + + /** + * Sets the list of disabled tuners + */ + public void setDisabledTuners(List disabledTuners) + { + mDisabledTuners = disabledTuners; + } + + /** + * Get list of tuner configurations + */ + public List getTunerConfigurations() + { + return mTunerConfigurations; + } + + /** + * Sets the list of tuner configurations + */ + public void setTunerConfigurations(List tunerConfigurations) + { + mTunerConfigurations = tunerConfigurations; + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java index 2172e215..3378e7bb 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java @@ -1,41 +1,48 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.fcd; -import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.source.ChannelizerType; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; +/** + * Funcube Dongle Tuner base class + */ public class FCDTuner extends Tuner { - public FCDTuner(FCDTunerController controller, UserPreferences userPreferences) + /** + * Constructs an instance + * @param controller for the tuner + * @param tunerErrorListener to receive error notifications + */ + public FCDTuner(FCDTunerController controller, ITunerErrorListener tunerErrorListener) { - super(controller.getConfiguration().toString(), controller, userPreferences); + super(controller, tunerErrorListener, ChannelizerType.HETERODYNE); } - public void dispose() + @Override + public String getPreferredName() { - getController().dispose(); + return getTunerType() + " " + getUniqueID(); } public FCDTunerController getController() diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java index 2a1612e3..83ae087d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java @@ -23,16 +23,18 @@ import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.adapter.ComplexShortAdapter; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.mixer.ComplexMixer; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.MixerTunerType; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerController; import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.usb4java.Context; import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import org.usb4java.DeviceHandle; +import org.usb4java.DeviceList; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; @@ -47,60 +49,74 @@ import java.util.concurrent.locks.ReentrantLock; public abstract class FCDTunerController extends TunerController { private final static Logger mLog = LoggerFactory.getLogger(FCDTunerController.class); + private static final double USABLE_BANDWIDTH_PERCENT = 1.0; + private static final int DC_SPIKE_AVOID_HALF_BANDWIDTH = 5000; + private final static byte FCD_INTERFACE = (byte) 0x2; + private final static byte FCD_ENDPOINT_IN = (byte) 0x82; + private final static byte FCD_ENDPOINT_OUT = (byte) 0x2; - public static final double USABLE_BANDWIDTH_PERCENT = 1.0; - public static final int DC_SPIKE_AVOID_BUFFER = 5000; - - public final static byte FCD_INTERFACE = (byte)0x2; - public final static byte FCD_ENDPOINT_IN = (byte)0x82; - public final static byte FCD_ENDPOINT_OUT = (byte)0x2; - + private int mBus; + private int mPort; + private Context mDeviceContext = new Context(); private Device mDevice; - private DeviceDescriptor mDeviceDescriptor; - private DeviceHandle mDeviceHandle; + private DeviceDescriptor mDeviceDescriptor = new DeviceDescriptor(); + private DeviceHandle mDeviceHandle = new DeviceHandle(); private FCDConfiguration mConfiguration = new FCDConfiguration(); protected ComplexMixer mComplexMixer; - protected ReentrantLock mLock = new ReentrantLock(); - + private ReentrantLock mListenerLock = new ReentrantLock(); /** * Generic FCD tuner controller - contains functionality common across both * funcube dongle tuners * - * @param device - * @param descriptor - * @param minTunableFrequency - * @param maxTunableFrequency + * @param tunerType of FCD + * @param mixerTDL for the sound card mixer interface + * @param bus usb + * @param port usb + * @param minTunableFrequency of the tuner + * @param maxTunableFrequency of the tuner + * @param tunerErrorListener to receive errors from this tuner */ - public FCDTunerController(MixerTunerType tunerType, TargetDataLine mixerTDL, Device device, - DeviceDescriptor descriptor, int minTunableFrequency, int maxTunableFrequency) + public FCDTunerController(MixerTunerType tunerType, TargetDataLine mixerTDL, int bus, int port, + int minTunableFrequency, int maxTunableFrequency, ITunerErrorListener tunerErrorListener) { - super(minTunableFrequency, maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); - mDevice = device; - mDeviceDescriptor = descriptor; + super(tunerErrorListener); + mBus = bus; + mPort = port; + setMinimumFrequency(minTunableFrequency); + setMaximumFrequency(maxTunableFrequency); + setMiddleUnusableHalfBandwidth(DC_SPIKE_AVOID_HALF_BANDWIDTH); + setUsableBandwidthPercentage(USABLE_BANDWIDTH_PERCENT); try { - mFrequencyController.setSampleRate((int)tunerType.getAudioFormat().getSampleRate()); + mFrequencyController.setSampleRate((int) tunerType.getAudioFormat().getSampleRate()); } catch(SourceException se) { mLog.error("Error setting sample rate to [" + tunerType.getAudioFormat().getSampleRate() + "]", se); } - mComplexMixer = new ComplexMixer( mixerTDL, tunerType.getAudioFormat(), tunerType.getDisplayString(), - new ComplexShortAdapter()); - - mComplexMixer.setBufferSize(tunerType.getAudioFormat().getFrameSize() * getBufferSampleCount()); - + mComplexMixer = new ComplexMixer(mixerTDL, tunerType.getAudioFormat(), tunerType.getDisplayString(), + new ComplexShortAdapter()); + mComplexMixer.setBufferSampleCount(getBufferSampleCount()); mComplexMixer.setBufferListener(mNativeBufferBroadcaster); } @Override public int getBufferSampleCount() { - return (int)(mComplexMixer.getAudioFormat().getSampleRate() * 0.1); + if(getTunerType() == TunerType.FUNCUBE_DONGLE_PRO) + { + return 4096; + } + else if(getTunerType() == TunerType.FUNCUBE_DONGLE_PRO_PLUS) + { + return 8192; + } + + throw new IllegalStateException("Unrecognized tuner type: " + getTunerType()); } /** @@ -109,13 +125,20 @@ public abstract class FCDTunerController extends TunerController @Override public void addBufferListener(Listener listener) { - boolean hasExistingListeners = hasBufferListeners(); + mListenerLock.lock(); - super.addBufferListener(listener); - - if(!hasExistingListeners) + try { - mComplexMixer.start(); + boolean hasExistingListeners = hasBufferListeners(); + super.addBufferListener(listener); + if(!hasExistingListeners) + { + mComplexMixer.start(); + } + } + finally + { + mListenerLock.unlock(); } } @@ -126,44 +149,156 @@ public abstract class FCDTunerController extends TunerController @Override public void removeBufferListener(Listener listener) { - super.removeBufferListener(listener); + mListenerLock.lock(); - if(!hasBufferListeners()) + try { - mComplexMixer.stop(); + super.removeBufferListener(listener); + if(!hasBufferListeners()) + { + ComplexMixer complexMixer = mComplexMixer; + + if(complexMixer != null) + { + complexMixer.stop(); + } + } + } + finally + { + mListenerLock.unlock(); } } /** - * Initializes the controller by opening the USB device and claiming the - * HID interface. - * - * Invoke this method after constructing this class to setup the - * controller. - * - * @throws SourceException if cannot open and claim the USB device + * Finds the USB device for this tuner at the specified USB bus and port. + * @return discovered USB device + * @throws SourceException if there is an error or the device is not discovered. */ - public void init() throws SourceException + private Device findDevice() throws SourceException { - mDeviceHandle = new DeviceHandle(); + DeviceList deviceList = new DeviceList(); + int count = LibUsb.getDeviceList(mDeviceContext, deviceList); - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) + if(count >= 0) { - mDeviceHandle = null; + for(Device device: deviceList) + { + int bus = LibUsb.getBusNumber(device); + int port = LibUsb.getPortNumber(device); - throw new SourceException("libusb couldn't open funcube usb device [" + LibUsb.errorName(result) + "]"); + if(mBus == bus && mPort == port) + { + return device; + } + } } - claimInterface(); + throw new SourceException("LibUsb couldn't discover USB device [" + mBus + ":" + mPort + + "] from device list" + (count < 0 ? " - error: " + LibUsb.errorName(count) : "")); } + /** + * Initializes the controller by opening the USB device and claiming the HID interface. + * Invoke this method after constructing this class to setup the controller. + * + * @throws SourceException when it cannot open and claim the USB device + */ + public void start() throws SourceException + { + if(mDeviceContext == null) + { + throw new SourceException("Device cannot be reused once it has been shutdown"); + } + + int status = LibUsb.init(mDeviceContext); + + if(status != LibUsb.SUCCESS) + { + throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status)); + } + + mDevice = findDevice(); + mDeviceDescriptor = new DeviceDescriptor(); + status = LibUsb.getDeviceDescriptor(mDevice, mDeviceDescriptor); + + if(status != LibUsb.SUCCESS) + { + mDeviceDescriptor = null; + throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status)); + } + + mDeviceHandle = new DeviceHandle(); + status = LibUsb.open(mDevice, mDeviceHandle); + + if(status != LibUsb.SUCCESS) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + + mLog.error("Can't open USB tuner [" + getTunerType() + "] - check driver or Linux udev rules"); + throw new SourceException("Can't open USB tuner - check driver or Linux udev rules - " + LibUsb.errorName(status)); + } + + //Remove a kernel driver if active + status = LibUsb.kernelDriverActive(mDeviceHandle, FCD_INTERFACE); + + if(status == 1) + { + status = LibUsb.detachKernelDriver(mDeviceHandle, FCD_INTERFACE); + + if(status != LibUsb.SUCCESS) + { + mLog.error("Unable to detach kernel driver for USB tuner device - bus:" + mBus + " port:" + mPort); + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("Can't detach kernel driver"); + } + } + + //Claim the interface + status = LibUsb.claimInterface(mDeviceHandle, FCD_INTERFACE); + + if(status == LibUsb.ERROR_BUSY) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("USB tuner is in-use by another application"); + } + else if(status != LibUsb.SUCCESS) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("Can't claim interface on USB tuner - " + LibUsb.errorName(status)); + } + + try + { + deviceStart(); + } + catch(SourceException se) + { + throw se; + } + } + + /** + * Startup actions for the sub-class implementation + * @throws SourceException if there is an error + */ + protected abstract void deviceStart() throws SourceException; + /** * Disposes of resources. Closes the USB device and interface. */ - public void dispose() + public void stop() { + if(mComplexMixer != null) + { + mComplexMixer.stop(); + mComplexMixer = null; + } + if(mDeviceHandle != null) { try @@ -174,12 +309,13 @@ public abstract class FCDTunerController extends TunerController { mLog.error("error while closing device handle", e); } - - mDeviceHandle = null; } mDeviceDescriptor = null; + mDeviceHandle = null; mDevice = null; + LibUsb.exit(mDeviceContext); + mDeviceContext = null; } /** @@ -197,11 +333,6 @@ public abstract class FCDTunerController extends TunerController */ public abstract TunerType getTunerType(); - /** - * Applies the settings in the tuner configuration - */ - public abstract void apply(TunerConfiguration config) throws SourceException; - /** * USB address (bus/port) */ @@ -234,9 +365,9 @@ public abstract class FCDTunerController extends TunerController { StringBuilder sb = new StringBuilder(); - sb.append(String.format("%04X", (int)(mDeviceDescriptor.idVendor() & 0xFFFF))); + sb.append(String.format("%04X", (int) (mDeviceDescriptor.idVendor() & 0xFFFF))); sb.append(":"); - sb.append(String.format("%04X", (int)(mDeviceDescriptor.idProduct() & 0xFFFF))); + sb.append(String.format("%04X", (int) (mDeviceDescriptor.idProduct() & 0xFFFF))); return sb.toString(); } @@ -304,7 +435,7 @@ public abstract class FCDTunerController extends TunerController */ public void setTunedFrequency(long frequency) throws SourceException { - mLock.lock(); + mListenerLock.lock(); try { @@ -313,11 +444,11 @@ public abstract class FCDTunerController extends TunerController catch(Exception e) { throw new SourceException("Couldn't set FCD Local " + - "Oscillator Frequency [" + frequency + "]", e); + "Oscillator Frequency [" + frequency + "]", e); } finally { - mLock.unlock(); + mListenerLock.unlock(); } } @@ -329,15 +460,12 @@ public abstract class FCDTunerController extends TunerController try { ByteBuffer buffer = send(FCDCommand.APP_GET_FREQUENCY_HZ); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - return (int)(buffer.getInt(2) & 0xFFFFFFFF); + return (int) (buffer.getInt(2) & 0xFFFFFFFF); } catch(Exception e) { - throw new SourceException("FCDTunerController - " - + "couldn't get LO frequency", e); + throw new SourceException("FCDTunerController - couldn't get LO frequency", e); } } @@ -349,39 +477,6 @@ public abstract class FCDTunerController extends TunerController return mConfiguration; } - /** - * Claims the USB interface. Attempts to detach the active kernel driver if - * one is currently attached. - */ - private void claimInterface() throws SourceException - { - if(mDeviceHandle != null) - { - int result = LibUsb.kernelDriverActive(mDeviceHandle, FCD_INTERFACE); - - if(result == 1) - { - result = LibUsb.detachKernelDriver(mDeviceHandle, FCD_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + LibUsb.errorName(result) + "]"); - } - } - - result = LibUsb.claimInterface(mDeviceHandle, FCD_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + LibUsb.errorName(result) + "]"); - } - } - else - { - throw new SourceException("couldn't claim usb hid interface - no device handle"); - } - } - /** * Performs an interrupt write to the OUT endpoint. * @@ -393,7 +488,6 @@ public abstract class FCDTunerController extends TunerController if(mDeviceHandle != null) { IntBuffer transferred = IntBuffer.allocate(1); - int result = LibUsb.interruptTransfer(mDeviceHandle, FCD_ENDPOINT_OUT, buffer, transferred, 500l); if(result != LibUsb.SUCCESS) @@ -416,37 +510,11 @@ public abstract class FCDTunerController extends TunerController private void write(FCDCommand command) throws LibUsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(64); - buffer.put(0, command.getCommand()); - buffer.put(1, (byte)0x00); - + buffer.put(1, (byte) 0x00); write(buffer); } - /** - * Convenience logger for debugging read/write operations - */ - @SuppressWarnings("unused") - private void log(String label, ByteBuffer buffer) - { - StringBuilder sb = new StringBuilder(); - - sb.append(label); - - sb.append(" "); - - sb.append(buffer.get(0)); - sb.append(" | "); - - for(int x = 0; x < 64; x++) - { - sb.append(String.format("%02X", (int)(buffer.get(x) & 0xFF))); - sb.append(" "); - } - - mLog.debug(sb.toString()); - } - /** * Performs an interrupt write to the OUT endpoint for the FCD command. * @@ -457,13 +525,10 @@ public abstract class FCDTunerController extends TunerController private void write(FCDCommand command, long argument) throws LibUsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(64); - /* The FCD expects little-endian formatted values */ buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.put(0, command.getCommand()); buffer.putLong(1, argument); - write(buffer); } @@ -478,10 +543,7 @@ public abstract class FCDTunerController extends TunerController if(mDeviceHandle != null) { ByteBuffer buffer = ByteBuffer.allocateDirect(64); - IntBuffer transferred = IntBuffer.allocate(1); - - int result = LibUsb.interruptTransfer(mDeviceHandle, FCD_ENDPOINT_IN, buffer, transferred, 500l); if(result != LibUsb.SUCCESS) @@ -490,8 +552,8 @@ public abstract class FCDTunerController extends TunerController } else if(transferred.get(0) != buffer.capacity()) { - throw new LibUsbException("received bytes [" + transferred.get(0) + - "] didn't match expected length [" + buffer.capacity() + "]", result); + throw new LibUsbException("received bytes [" + transferred.get(0) + "] didn't match expected length [" + + buffer.capacity() + "]", result); } return buffer; @@ -508,7 +570,7 @@ public abstract class FCDTunerController extends TunerController * @param argument - command argument to send * @throws LibUsbException - on error */ - protected void send(FCDCommand command, long argument) throws LibUsbException + protected synchronized void send(FCDCommand command, long argument) throws LibUsbException { write(command, argument); read(); @@ -555,7 +617,6 @@ public abstract class FCDTunerController extends TunerController if(buffer.capacity() == 64) { byte[] data = new byte[64]; - for(int x = 0; x < 64; x++) { data[x] = buffer.get(x); @@ -680,7 +741,7 @@ public abstract class FCDTunerController extends TunerController private String mLabel; - private Block(String label) + Block(String label) { mLabel = label; } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java index 79e9a1a4..b6afe8e9 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java @@ -1,22 +1,24 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.fcd.proV1; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; @@ -26,12 +28,10 @@ import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.MixerGain; public class FCD1TunerConfiguration extends TunerConfiguration { - private double mFrequencyCorrection = 22.0d; private double mInphaseDCCorrection = 0.0d; private double mQuadratureDCCorrection = 0.0d; private double mPhaseCorrection = 0.0d; private double mGainCorrection = 0.0d; - private LNAGain mLNAGain = LNAGain.LNA_GAIN_PLUS_20_0; private LNAEnhance mLNAEnhance = LNAEnhance.LNA_ENHANCE_OFF; private MixerGain mMixerGain = MixerGain.MIXER_GAIN_PLUS_12_0; @@ -43,12 +43,12 @@ public class FCD1TunerConfiguration extends TunerConfiguration { } - public FCD1TunerConfiguration(String uniqueID, String name) + public FCD1TunerConfiguration(String uniqueID) { - super(uniqueID, name); + super(uniqueID); } - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @JsonIgnore @Override public TunerType getTunerType() { @@ -88,17 +88,6 @@ public class FCD1TunerConfiguration extends TunerConfiguration mMixerGain = gain; } - @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection(double value) - { - mFrequencyCorrection = value; - } - @JacksonXmlProperty(isAttribute = true, localName = "inphase_dc_correction") public double getInphaseDCCorrection() { diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java index 10fb6c4b..95cab56f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java @@ -1,23 +1,25 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.fcd.proV1; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.MixerTunerType; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; @@ -26,8 +28,6 @@ import io.github.dsheirer.source.tuner.fcd.FCDCommand; import io.github.dsheirer.source.tuner.fcd.FCDTunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; import javax.sound.sampled.TargetDataLine; import javax.usb.UsbClaimException; @@ -35,14 +35,15 @@ import javax.usb.UsbException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +/** + * Funcube Dongle Pro tuner controller. Combines USB HID control with Audio Mixer data streaming interface. + */ public class FCD1TunerController extends FCDTunerController { private final static Logger mLog = LoggerFactory.getLogger(FCD1TunerController.class); - public static final int MINIMUM_TUNABLE_FREQUENCY = 64000000; public static final int MAXIMUM_TUNABLE_FREQUENCY = 1700000000; public static final int SAMPLE_RATE = 96000; - private double mDCCorrectionInPhase = 0.0; private double mDCCorrectionQuadrature = 0.0; private double mPhaseCorrection = 0.0; @@ -50,19 +51,22 @@ public class FCD1TunerController extends FCDTunerController private LNAGain mLNAGain; private LNAEnhance mLNAEnhance; private MixerGain mMixerGain; - private FCD1TunerConfiguration mTunerConfiguration; - private FCD1TunerEditor mEditor; - public FCD1TunerController(TargetDataLine mixerTDL, Device device, DeviceDescriptor descriptor) + /** + * Constructs an instance + * @param mixerTDL for the audio/data interface + * @param bus usb + * @param port usb + * @param tunerErrorListener to receive tuner errors + */ + public FCD1TunerController(TargetDataLine mixerTDL, int bus, int port, ITunerErrorListener tunerErrorListener) { - super(MixerTunerType.FUNCUBE_DONGLE_PRO, mixerTDL, device, descriptor, - MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY); + super(MixerTunerType.FUNCUBE_DONGLE_PRO, mixerTDL, bus, port, MINIMUM_TUNABLE_FREQUENCY, + MAXIMUM_TUNABLE_FREQUENCY, tunerErrorListener); } - public void init() throws SourceException + protected void deviceStart() throws SourceException { - super.init(); - mFrequencyController.setSampleRate(SAMPLE_RATE); try @@ -73,7 +77,6 @@ public class FCD1TunerController extends FCDTunerController getLNAGainSetting(); getLNAEnhanceSetting(); getMixerGainSetting(); - send(FCDCommand.APP_SET_MIXER_GAIN, 1l); } catch(Exception e) @@ -99,73 +102,45 @@ public class FCD1TunerController extends FCDTunerController return TunerType.FUNCUBE_DONGLE_PRO; } - /** - * Get the tuner configuration - * - * @return current config or null if one hasn't been applied yet - */ - public FCD1TunerConfiguration getTunerConfiguration() - { - return mTunerConfiguration; - } - @Override - public void apply(TunerConfiguration tunerConfig) - throws SourceException + public void apply(TunerConfiguration tunerConfig) throws SourceException { - FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig; + //Invoke super for frequency, frequency correction and autoPPM + super.apply(tunerConfig); - try + if(tunerConfig instanceof FCD1TunerConfiguration fcd1) { - setFrequencyCorrection(config.getFrequencyCorrection()); - setLNAGain(config.getLNAGain()); - setLNAEnhance(config.getLNAEnhance()); - setMixerGain(config.getMixerGain()); - setDCCorrectionInPhase(config.getInphaseDCCorrection()); - setDCCorrectionQuadrature(config.getQuadratureDCCorrection()); - setGainCorrection(config.getGainCorrection()); - setPhaseCorrection(config.getPhaseCorrection()); - - //If we get to here without exception, store this config - mTunerConfiguration = config; + try + { + setLNAGain(fcd1.getLNAGain()); + setLNAEnhance(fcd1.getLNAEnhance()); + setMixerGain(fcd1.getMixerGain()); + setDCCorrectionInPhase(fcd1.getInphaseDCCorrection()); + setDCCorrectionQuadrature(fcd1.getQuadratureDCCorrection()); + setGainCorrection(fcd1.getGainCorrection()); + setPhaseCorrection(fcd1.getPhaseCorrection()); + } + catch(Exception e) + { + //Re-cast any usb exceptions as source exceptions + throw new SourceException("FCD 1 Controller error applying tuner config - " + e.getLocalizedMessage()); + } } - catch(Exception e) - { - //Re-cast any usb exceptions as source exceptions - throw new SourceException("FCD 1 Controller encountered an " - + "exception while applying tuner config - " + - e.getLocalizedMessage()); - } - - try - { - setFrequency(config.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } + } /** * Gets the current LNA Gain value from the controller and stores it */ - private void getLNAGainSetting() throws UsbClaimException, UsbException + public LNAGain getLNAGainSetting() throws UsbClaimException, UsbException { - ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_GAIN); + if(mLNAGain == null) + { + ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_GAIN); + buffer.order(ByteOrder.LITTLE_ENDIAN); + int gain = buffer.getInt(2); + mLNAGain = LNAGain.valueOf(gain); + } - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int gain = buffer.getInt(2); - - mLNAGain = LNAGain.valueOf(gain); - } - - /** - * @return - current lna gain setting - */ - public LNAGain getLNAGain() - { return mLNAGain; } @@ -178,16 +153,7 @@ public class FCD1TunerController extends FCDTunerController */ public void setLNAGain(LNAGain gain) throws UsbException, UsbClaimException { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting()); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting()); } /** @@ -206,13 +172,11 @@ public class FCD1TunerController extends FCDTunerController * @throws UsbClaimException - if cannot claim the FCD HID controller * @throws UsbException - if there was a usb error */ - public void setPhaseCorrection(double value) - throws UsbClaimException, UsbException + public void setPhaseCorrection(double value) throws UsbClaimException, UsbException { if(value < -1.0 || value > 1.0) { - throw new IllegalArgumentException("Phase correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )"); + throw new IllegalArgumentException("Phase correction value [" + value + "] outside limits ( -1.0 <> 1.0 )"); } mPhaseCorrection = value; @@ -238,8 +202,7 @@ public class FCD1TunerController extends FCDTunerController { if(value < -1.0 || value > 1.0) { - throw new IllegalArgumentException("Gain correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )"); + throw new IllegalArgumentException("Gain correction value [" + value + "] outside limits ( -1.0 <> 1.0 )"); } mGainCorrection = value; @@ -249,73 +212,47 @@ public class FCD1TunerController extends FCDTunerController /** * Sends the phase and gain correction values to FCD. * - * The interface expects an argument with the SIGNED phase value in the first - * 16 bits and the UNSIGNED gain value in the second 16 bits, both in - * big-endian format. Since we're sending the argument in java little endian - * format, we place the phase in the high-order bits and the gain in the - * low-order bits, so that the send(command) function will reorder the bytes - * into big endian format, and place the arguments correctly. + * The interface expects an argument with the SIGNED phase value in the first 16 bits and the UNSIGNED gain value + * in the second 16 bits, both in big-endian format. Since we're sending the argument in java little endian + * format, we place the phase in the high-order bits and the gain in the low-order bits, so that the send(command) + * function will reorder the bytes into big endian format, and place the arguments correctly. * - * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle - * as values 0 to 65534 - * - * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the - * dongle as signed values -32768 to 32767 + * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle as values 0 to 65534 + * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the dongle as signed values -32768 to 32767 */ - private void setPhaseAndGainCorrection() throws UsbClaimException, - UsbException + private void setPhaseAndGainCorrection() throws UsbClaimException, UsbException { //UNSIGNED short gain value - masked into a long to avoid sign-extension long gain = (long)((1.0 + mGainCorrection) * Short.MAX_VALUE); - //Left shift gain to place value in upper 16 bits long longGain = Long.rotateLeft((0xFFFF & gain), 16); - //SIGNED short phase value short phase = (short)(mPhaseCorrection * Short.MAX_VALUE); - //Merge the results long correction = longGain | phase; - - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_IQ_CORRECTION, correction); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_IQ_CORRECTION, correction); } /** * Retrieves the stored phase and gain correction values from the FCD. * - * Note: testing shows that when the dongle is unplugged, any stored phase - * and gain correction values are reset. Reading the dongle after a fresh - * plugin shows the values reset. + * Note: testing shows that when the dongle is unplugged, any stored phase and gain correction values are reset. + * Reading the dongle after a fresh plugin shows the values reset. * * @throws UsbClaimException * @throws UsbException */ - private void getPhaseAndGainCorrection() throws UsbClaimException, - UsbException + private void getPhaseAndGainCorrection() throws UsbClaimException, UsbException { ByteBuffer buffer = send(FCDCommand.APP_GET_IQ_CORRECTION); - buffer.order(ByteOrder.LITTLE_ENDIAN); - int correction = buffer.getInt(2); /** - * Gain: mask the upper 16 phase bits and right shift to get the - * unsigned short value and then divide by 32768 to get the double - * value. We place the unsigned short value in an int to avoid sign - * issues. + * Gain: mask the upper 16 phase bits and right shift to get the unsigned short value and then divide by 32768 + * to get the double value. We place the unsigned short value in an int to avoid sign issues. */ int gain = (int)(Long.rotateRight(correction & 0xFFFF0000, 16)); - mGainCorrection = ((double)gain / (double)Short.MAX_VALUE) - 1.0; /** @@ -323,7 +260,6 @@ public class FCD1TunerController extends FCDTunerController * value */ short phase = (short)(correction & 0x0000FFFF); - mPhaseCorrection = ((double)phase / (double)Short.MAX_VALUE); } @@ -336,16 +272,14 @@ public class FCD1TunerController extends FCDTunerController } /** - * Sets DC correction for the I (inphase) component and sends the new value - * to the FCD controller + * Sets DC correction for the I (inphase) component and sends the new value to the FCD controller * * @param value - new DC correction value for the inphase component * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) * @throws UsbClaimException - if cannot claim the FCD HID controller * @throws UsbException - if there was a usb error */ - public void setDCCorrectionInPhase(double value) - throws UsbClaimException, UsbException + public void setDCCorrectionInPhase(double value) throws UsbClaimException, UsbException { if(value < -1.0 || value > 1.0) { @@ -366,16 +300,14 @@ public class FCD1TunerController extends FCDTunerController } /** - * Sets the DC correction for the Q (quadrature) component and send the - * new value to the FCD controller + * Sets the DC correction for the Q (quadrature) component and send the new value to the FCD controller * * @param value - new DC correction value for the quadrature component * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) * @throws UsbClaimException - if cannot claim the FCD HID controller * @throws UsbException - if there was a usb error */ - public void setDCCorrectionQuadrature(double value) - throws UsbClaimException, UsbException + public void setDCCorrectionQuadrature(double value) throws UsbClaimException, UsbException { if(value < -1.0 || value > 1.0) { @@ -390,56 +322,37 @@ public class FCD1TunerController extends FCDTunerController /** * Sends the I/Q DC offset correction values to FCD. * - * The interface expects an argument with the signed inphase value in the - * first 16 bits and the signed quadrature value in the second 16 bits, - * both in big-endian format. + * The interface expects an argument with the signed inphase value in the first 16 bits and the signed quadrature + * value in the second 16 bits, both in big-endian format. * - * Since we're sending the argument in java little endian format, we place - * the quadrature in the high-order bits and the inphase in the low-order - * bits, so that the send(command) function will reorder the bytes into big + * Since we're sending the argument in java little endian format, we place the quadrature in the high-order bits + * and the inphase in the low-order bits, so that the send(command) function will reorder the bytes into big * endian format, and place the arguments correctly. * - * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 - * and are applied to the dongle as signed short values -32768 to 0 to 32767 + * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 and are applied to the dongle as signed + * short values -32768 to 0 to 32767 */ private void setDCIQCorrection() throws UsbClaimException, UsbException { //I & Q DC offset correction values are signed short values short inphase = (short)(mDCCorrectionInPhase * Short.MAX_VALUE); short quadrature = (short)(mDCCorrectionQuadrature * Short.MAX_VALUE); - //Mask the shorts into longs to preserve the sign bit and prepare //for merging to a single 32-bit value long maskedInphase = inphase & 0x0000FFFF; long maskedQuadrature = quadrature & 0x0000FFFF; - //Left shift quadrature to place value in upper 16 bits long shiftedQuadrature = Long.rotateLeft(maskedQuadrature, 16); - //Merge the results long correction = shiftedQuadrature | maskedInphase; - - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_DC_CORRECTION, correction); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_DC_CORRECTION, correction); } - private void getDCIQCorrection() throws UsbClaimException, - UsbException + private void getDCIQCorrection() throws UsbClaimException, UsbException { ByteBuffer buffer = send(FCDCommand.APP_GET_DC_CORRECTION); - buffer.order(ByteOrder.LITTLE_ENDIAN); - int correction = buffer.getInt(2); - /** * Quadrature: mask the upper 16 bits and divide by 32768 to get the * stored quadrature value. Cast the 16-bit value to a short to get @@ -447,9 +360,7 @@ public class FCD1TunerController extends FCDTunerController */ long shiftedQuadrature = correction & 0xFFFF0000; long quadrature = Long.rotateRight(shiftedQuadrature, 16); - mDCCorrectionQuadrature = ((short)quadrature / (double)Short.MAX_VALUE); - /** * InPhase: mask the lower 16 bits to get the short * value and then divide by 32768 to get the stored inphase value. @@ -457,23 +368,13 @@ public class FCD1TunerController extends FCDTunerController */ long inphase = correction & 0x0000FFFF; mDCCorrectionInPhase = ((short)inphase / (double)Short.MAX_VALUE); - -// Log.info( "FCD1:" + -// " correction:" + correction + -// " Q:" + quadrature + -// " I:" + inphase + -// " QCorr:" + mDCCorrectionQuadrature + -// " ICorr:" + mDCCorrectionInPhase ); } private void getLNAEnhanceSetting() throws UsbClaimException, UsbException { ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_ENHANCE); - buffer.order(ByteOrder.LITTLE_ENDIAN); - int enhance = buffer.getInt(2); - mLNAEnhance = LNAEnhance.valueOf(enhance); } @@ -484,26 +385,14 @@ public class FCD1TunerController extends FCDTunerController public void setLNAEnhance(LNAEnhance enhance) throws UsbClaimException, UsbException { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting()); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting()); } private void getMixerGainSetting() throws UsbClaimException, UsbException { ByteBuffer buffer = send(FCDCommand.APP_GET_MIXER_GAIN); - buffer.order(ByteOrder.LITTLE_ENDIAN); - int gain = buffer.getInt(2); - mMixerGain = MixerGain.valueOf(gain); } @@ -514,16 +403,7 @@ public class FCD1TunerController extends FCDTunerController public void setMixerGain(MixerGain gain) throws UsbClaimException, UsbException { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting()); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting()); } public enum Block @@ -677,9 +557,8 @@ public class FCD1TunerController extends FCDTunerController case 7: return LNA_ENHANCE_3; default: - throw new IllegalArgumentException("FCD 1.0 Tuner " - + "Controller - unrecognized LNA enhance setting " - + "value [" + setting + "]"); + throw new IllegalArgumentException("FCD 1.0 Tuner Controller - unrecognized LNA enhance setting " + + "value [" + setting + "]"); } } @@ -725,8 +604,7 @@ public class FCD1TunerController extends FCDTunerController case 1: return MIXER_GAIN_PLUS_12_0; default: - throw new IllegalArgumentException("FCD 1.0 Tuner " - + "Controller - unrecognized mixer gain setting " + throw new IllegalArgumentException("FCD 1.0 Tuner Controller - unrecognized mixer gain setting " + "value [" + setting + "]"); } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java index 2e2e2f79..acecc5c4 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -1,32 +1,31 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.fcd.proV1; -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent.Event; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.tuner.fcd.FCDTuner; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.LNAGain; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.MixerGain; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,332 +36,296 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; import javax.swing.JSpinner; -import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.text.DecimalFormat; -public class FCD1TunerEditor extends TunerConfigurationEditor +/** + * Funcube Dongle Pro tuner editor + */ +public class FCD1TunerEditor extends TunerEditor { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger(FCD1TunerEditor.class); - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JSpinner mFrequencyCorrection; - private JComboBox mComboLNAGain; - private JComboBox mComboLNAEnhance; - private JComboBox mComboMixerGain; + private JButton mTunerInfoButton; + private JComboBox mLnaGainCombo; + private JComboBox mLnaEnhanceCombo; + private JComboBox mMixerGainCombo; private CorrectionSpinner mCorrectionDCI; private CorrectionSpinner mCorrectionDCQ; - private CorrectionSpinner mCorrectionGain; - private CorrectionSpinner mCorrectionPhase; - private boolean mLoading; + private CorrectionSpinner mGainCorrectionSpinner; + private CorrectionSpinner mPhaseCorrectionSpinner; - private FCD1TunerController mController; - - public FCD1TunerEditor(TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner) + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager to save configuration + * @param discoveredTuner to control + */ + public FCD1TunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) { - super(tunerConfigurationModel); - mController = (FCD1TunerController)tuner.getController(); - + super(userPreferences, tunerManager, discoveredTuner); init(); + tunerStatusUpdated(); } - private FCD1TunerConfiguration getConfiguration() + private FCD1TunerController getController() { - if(hasItem()) + if(hasTuner()) { - return (FCD1TunerConfiguration)getItem(); + return (FCD1TunerController)getTuner().getController(); } return null; } + @Override + protected void tunerStatusUpdated() + { + setLoading(true); + + if(hasTuner()) + { + getTunerIdLabel().setText(getTuner().getPreferredName()); + } + else + { + getTunerIdLabel().setText(getDiscoveredTuner().getId()); + } + + String status = getDiscoveredTuner().getTunerStatus().toString(); + if(getDiscoveredTuner().hasErrorMessage()) + { + status += " - " + getDiscoveredTuner().getErrorMessage(); + } + getTunerStatusLabel().setText(status); + getButtonPanel().updateControls(); + getFrequencyPanel().updateControls(); + getTunerInfoButton().setEnabled(hasTuner()); + + if(hasTuner() && hasConfiguration()) + { + getMixerGainCombo().setSelectedItem(getConfiguration().getMixerGain()); + getLnaEnhanceCombo().setSelectedItem(getConfiguration().getLNAEnhance()); + getLnaGainCombo().setSelectedItem(getConfiguration().getLNAGain()); + getDcCorrectionSpinnerI().setValue(getConfiguration().getInphaseDCCorrection()); + getDcCorrectionSpinnerQ().setValue(getConfiguration().getQuadratureDCCorrection()); + getPhaseCorrectionSpinner().setValue(getConfiguration().getGainCorrection()); + getGainCorrectionSpinner().setValue(getConfiguration().getPhaseCorrection()); + } + + getMixerGainCombo().setEnabled(hasTuner()); + getLnaEnhanceCombo().setEnabled(hasTuner()); + getLnaGainCombo().setEnabled(hasTuner()); + getDcCorrectionSpinnerI().setEnabled(hasTuner()); + getDcCorrectionSpinnerQ().setEnabled(hasTuner()); + getPhaseCorrectionSpinner().setEnabled(hasTuner()); + getGainCorrectionSpinner().setEnabled(hasTuner()); + + setLoading(false); + } + private void init() { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][][][][][grow]")); + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][][][][][][][][grow]")); - add(new JLabel("FCD Pro Tuner Configuration"), "span,align center"); + add(new JLabel("Tuner:")); + add(getTunerIdLabel()); + add(getTunerInfoButton()); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() - { - @Override - public void focusLost(FocusEvent e) - { - save(); - } + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); - @Override - public void focusGained(FocusEvent e) - { - } - }); + add(getButtonPanel(), "span,align left"); - add(new JLabel("Name:")); - add(mConfigurationName, "span 2"); + add(new JSeparator(), "span,growx,push"); + add(new JLabel("Frequency (MHz):")); + add(getFrequencyPanel(), "wrap"); - mTunerInfo = new JButton("Tuner Info"); - mTunerInfo.setEnabled(false); - mTunerInfo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - JOptionPane.showMessageDialog(FCD1TunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(mTunerInfo); - - SpinnerModel model = new SpinnerNumberModel(0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1); //step - - mFrequencyCorrection = new JSpinner(model); - mFrequencyCorrection.setEnabled(false); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits(1); - editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - - mFrequencyCorrection.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection(value); - save(); - } - catch(SourceException e1) - { - JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCD Pro Tuner " - + "Controller - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage()); - - mLog.error("FuncubeDonglePro Controller - couldn't apply " - + "frequency correction value: " + value, e1); - } - } - }); - - add(new JLabel("PPM:")); - add(mFrequencyCorrection, "wrap"); - - add(new JSeparator(JSeparator.HORIZONTAL), "span,grow"); + add(new JSeparator(), "span,growx,push"); add(new JLabel("Gain"), "span,align center"); - - mComboLNAGain = new JComboBox(LNAGain.values()); - mComboLNAGain.setEnabled(false); - mComboLNAGain.setToolTipText("Adjust the low noise amplifier gain setting."); - mComboLNAGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); - - - try - { - mController.setLNAGain(gain); - save(); - } - catch(Exception e) - { - JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCD Pro Tuner Controller" - + " - error setting LNA gain [" + gain.toString() + "]"); - - mLog.error("FuncubeDonglePro Controller - error setting gain [" + gain.toString() + "]", e); - } - } - }); - add(new JLabel("LNA:")); - add(mComboLNAGain); - - /** - * LNA Enhance - */ - mComboLNAEnhance = new JComboBox(LNAEnhance.values()); - mComboLNAEnhance.setToolTipText("Adjust the LNA enhance setting. Default value is OFF"); - - mComboLNAEnhance.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); - - try - { - mController.setLNAEnhance(enhance); - save(); - } - catch(Exception e1) - { - JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCD Pro Tuner" - + " error setting LNA enhance gain [" + enhance.toString() + "]"); - - mLog.error("FCDPro - error setting LNA enhance [" + enhance.toString() + "]", e1); - } - } - }); - + add(getLnaGainCombo(), "wrap"); add(new JLabel("Enhance:")); - add(mComboLNAEnhance); - - /** - * Mixer Gain - */ - mComboMixerGain = new JComboBox(MixerGain.values()); - mComboMixerGain.setToolTipText("Adjust mixer gain setting"); - - - mComboMixerGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); - - try - { - mController.setMixerGain(gain); - save(); - } - catch(Exception e1) - { - JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCDPro - error setting" - + " mixer gain [" + gain.toString() + "]"); - - mLog.error("FCDPro - error setting mixer gain [" + gain.toString() + "]", e1); - } - } - }); - + add(getLnaEnhanceCombo(), "wrap"); add(new JLabel("Mixer:")); - add(mComboMixerGain, "wrap"); + add(getMixerGainCombo(), "wrap"); - add(new JSeparator(JSeparator.HORIZONTAL), "span,grow"); + add(new JSeparator(), "span,growx,push"); add(new JLabel("Correction"), "span,align center"); - - /** - * Inphase DC Correction - */ - mCorrectionDCI = new CorrectionSpinner(Correction.DC_INPHASE, 0.0, 0.00001, 5); - mCorrectionDCI.setEnabled(false); - mCorrectionDCI.setToolTipText("DC Bias Correction/Inphase " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)"); add(new JLabel("DC Inphase:")); - add(mCorrectionDCI); - - /** - * Quadrature DC Correction - */ - mCorrectionDCQ = new CorrectionSpinner(Correction.DC_QUADRATURE, 0.0, 0.00001, 5); - mCorrectionDCQ.setToolTipText("DC Bias Correction/Quadrature " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)"); + add(getDcCorrectionSpinnerI(), "wrap"); add(new JLabel("DC Quadrature:")); - add(mCorrectionDCQ); - - /** - * Gain Correction - */ - mCorrectionGain = new CorrectionSpinner(Correction.GAIN, 0.0, 0.00001, 5); - mCorrectionGain.setToolTipText("Gain Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)"); + add(getDcCorrectionSpinnerQ(), "wrap"); add(new JLabel("Gain:")); - add(mCorrectionGain); - - /** - * Phase Correction - */ - mCorrectionPhase = new CorrectionSpinner(Correction.PHASE, 0.0, 0.00001, 5); - mCorrectionPhase.setToolTipText("Phase Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)"); + add(getGainCorrectionSpinner(), "wrap"); add(new JLabel("Phase:")); - add(mCorrectionPhase); + add(getPhaseCorrectionSpinner(), "wrap"); + } + + public JComboBox getLnaGainCombo() + { + if(mLnaGainCombo == null) + { + mLnaGainCombo = new JComboBox<>(LNAGain.values()); + mLnaGainCombo.setEnabled(false); + mLnaGainCombo.setToolTipText("Adjust the low noise amplifier gain setting."); + mLnaGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + LNAGain gain = (LNAGain) mLnaGainCombo.getSelectedItem(); + + try + { + getController().setLNAGain(gain); + save(); + } + catch(Exception e) + { + JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCD Pro Tuner " + + "Controller - error setting LNA gain [" + gain + "]"); + + mLog.error("FuncubeDonglePro Controller - error setting gain [" + gain + "]", e); + } + } + }); + } + + return mLnaGainCombo; + } + + public JComboBox getLnaEnhanceCombo() + { + if(mLnaEnhanceCombo == null) + { + mLnaEnhanceCombo = new JComboBox<>(LNAEnhance.values()); + mLnaEnhanceCombo.setEnabled(false); + mLnaEnhanceCombo.setToolTipText("Adjust the LNA enhance setting. Default value is OFF"); + mLnaEnhanceCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + LNAEnhance enhance = (LNAEnhance) mLnaEnhanceCombo.getSelectedItem(); + + try + { + getController().setLNAEnhance(enhance); + save(); + } + catch(Exception e1) + { + JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCD Pro Tuner" + + " error setting LNA enhance gain [" + enhance + "]"); + + mLog.error("FCDPro - error setting LNA enhance [" + enhance + "]", e1); + } + } + }); + } + + return mLnaEnhanceCombo; + } + + public JComboBox getMixerGainCombo() + { + if(mMixerGainCombo == null) + { + mMixerGainCombo = new JComboBox<>(MixerGain.values()); + mMixerGainCombo.setEnabled(false); + mMixerGainCombo.setToolTipText("Adjust mixer gain setting"); + mMixerGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + MixerGain gain = (MixerGain) mMixerGainCombo.getSelectedItem(); + + try + { + getController().setMixerGain(gain); + save(); + } + catch(Exception e1) + { + JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCDPro - error setting" + + " mixer gain [" + gain + "]"); + + mLog.error("FCDPro - error setting mixer gain [" + gain + "]", e1); + } + } + }); + } + + return mMixerGainCombo; + } + + public CorrectionSpinner getDcCorrectionSpinnerQ() + { + if(mCorrectionDCQ == null) + { + mCorrectionDCQ = new CorrectionSpinner(Correction.DC_QUADRATURE, 0.0, 0.00001, 5); + mCorrectionDCQ.setEnabled(false); + mCorrectionDCQ.setToolTipText("DC Bias Correction/Quadrature Component: valid values are -1.0 to 1.0 (default: 0.0)"); + } + + return mCorrectionDCQ; + } + + public CorrectionSpinner getDcCorrectionSpinnerI() + { + if(mCorrectionDCI == null) + { + mCorrectionDCI = new CorrectionSpinner(Correction.DC_INPHASE, 0.0, 0.00001, 5); + mCorrectionDCI.setEnabled(false); + mCorrectionDCI.setToolTipText("DC Bias Correction/Inphase Component: valid values are -1.0 to 1.0 (default: 0.0)"); + } + + return mCorrectionDCI; + } + + public CorrectionSpinner getGainCorrectionSpinner() + { + if(mGainCorrectionSpinner == null) + { + mGainCorrectionSpinner = new CorrectionSpinner(Correction.GAIN, 0.0, 0.00001, 5); + mGainCorrectionSpinner.setEnabled(false); + mGainCorrectionSpinner.setToolTipText("Gain Correction: valid values are -1.0 to 1.0 (default: 0.0)"); + } + + return mGainCorrectionSpinner; + } + + public CorrectionSpinner getPhaseCorrectionSpinner() + { + if(mPhaseCorrectionSpinner == null) + { + mPhaseCorrectionSpinner = new CorrectionSpinner(Correction.PHASE, 0.0, 0.00001, 5); + mPhaseCorrectionSpinner.setEnabled(false); + mPhaseCorrectionSpinner.setToolTipText("Phase Correction: valid values are -1.0 to 1.0 (default: 0.0)"); + } + + return mPhaseCorrectionSpinner; } @Override public void setTunerLockState(boolean locked) { - //no-op + super.setTunerLockState(locked); } - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled(boolean enabled) + public JButton getTunerInfoButton() { - if(mConfigurationName.isEnabled() != enabled) + if(mTunerInfoButton == null) { - mConfigurationName.setEnabled(enabled); + mTunerInfoButton = new JButton("Tuner Info"); + mTunerInfoButton.setEnabled(false); + mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(FCD1TunerEditor.this, + getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE)); } - if(mTunerInfo.isEnabled() != enabled) - { - mTunerInfo.setEnabled(enabled); - } - - if(mFrequencyCorrection.isEnabled() != enabled) - { - mFrequencyCorrection.setEnabled(enabled); - } - - if(mComboLNAEnhance.isEnabled() != enabled) - { - mComboLNAEnhance.setEnabled(enabled); - } - - if(mComboLNAGain.isEnabled() != enabled) - { - mComboLNAGain.setEnabled(enabled); - } - - if(mComboMixerGain.isEnabled() != enabled) - { - mComboMixerGain.setEnabled(enabled); - } - - if(mCorrectionDCI.isEnabled() != enabled) - { - mCorrectionDCI.setEnabled(enabled); - } - - if(mCorrectionDCQ.isEnabled() != enabled) - { - mCorrectionDCQ.setEnabled(enabled); - } - - if(mCorrectionGain.isEnabled() != enabled) - { - mCorrectionGain.setEnabled(enabled); - } - - if(mCorrectionPhase.isEnabled() != enabled) - { - mCorrectionPhase.setEnabled(enabled); - } + return mTunerInfoButton; } private String getTunerInfo() @@ -371,25 +334,28 @@ public class FCD1TunerEditor extends TunerConfigurationEditor sb.append("

Funcube Dongle Pro Tuner

"); - sb.append("USB ID: "); - sb.append(mController.getUSBID()); - sb.append("
"); + if(hasTuner()) + { + sb.append("USB ID: "); + sb.append(getController().getUSBID()); + sb.append("
"); - sb.append("USB Address: "); - sb.append(mController.getUSBAddress()); - sb.append("
"); + sb.append("USB Address: "); + sb.append(getController().getUSBAddress()); + sb.append("
"); - sb.append("USB Speed: "); - sb.append(mController.getUSBSpeed()); - sb.append("
"); + sb.append("USB Speed: "); + sb.append(getController().getUSBSpeed()); + sb.append("
"); - sb.append("Cellular Band: "); - sb.append(mController.getConfiguration().getBandBlocking()); - sb.append("
"); + sb.append("Cellular Band: "); + sb.append(getController().getConfiguration().getBandBlocking()); + sb.append("
"); - sb.append("Firmware: "); - sb.append(mController.getConfiguration().getFirmware()); - sb.append("
"); + sb.append("Firmware: "); + sb.append(getController().getConfiguration().getFirmware()); + sb.append("
"); + } return sb.toString(); } @@ -397,81 +363,28 @@ public class FCD1TunerEditor extends TunerConfigurationEditor @Override public void save() { - if(hasItem() && !mLoading) + if(hasConfiguration() && !isLoading()) { - FCD1TunerConfiguration config = getConfiguration(); - - config.setName(mConfigurationName.getText()); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection(value); - config.setLNAEnhance((LNAEnhance)mComboLNAEnhance.getSelectedItem()); - config.setLNAGain((LNAGain)mComboLNAGain.getSelectedItem()); - config.setMixerGain((MixerGain)mComboMixerGain.getSelectedItem()); - + getConfiguration().setFrequency(getFrequencyControl().getFrequency()); + double value = ((SpinnerNumberModel) getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); + getConfiguration().setFrequencyCorrection(value); + getConfiguration().setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected()); + getConfiguration().setLNAEnhance((LNAEnhance) mLnaEnhanceCombo.getSelectedItem()); + getConfiguration().setLNAGain((LNAGain) mLnaGainCombo.getSelectedItem()); + getConfiguration().setMixerGain((MixerGain) mMixerGainCombo.getSelectedItem()); double dci = ((SpinnerNumberModel)mCorrectionDCI.getModel()).getNumber().doubleValue(); - config.setInphaseDCCorrection(dci); - + getConfiguration().setInphaseDCCorrection(dci); double dcq = ((SpinnerNumberModel)mCorrectionDCQ.getModel()).getNumber().doubleValue(); - config.setQuadratureDCCorrection(dcq); - - double gain = ((SpinnerNumberModel)mCorrectionGain.getModel()).getNumber().doubleValue(); - config.setGainCorrection(gain); - - double phase = ((SpinnerNumberModel)mCorrectionPhase.getModel()).getNumber().doubleValue(); - config.setPhaseCorrection(phase); - - getTunerConfigurationModel().broadcast(new TunerConfigurationEvent(getConfiguration(), Event.CHANGE)); + getConfiguration().setQuadratureDCCorrection(dcq); + double gain = ((SpinnerNumberModel) mGainCorrectionSpinner.getModel()).getNumber().doubleValue(); + getConfiguration().setGainCorrection(gain); + double phase = ((SpinnerNumberModel) mPhaseCorrectionSpinner.getModel()).getNumber().doubleValue(); + getConfiguration().setPhaseCorrection(phase); + saveConfiguration(); } } - @Override - public void setItem(TunerConfiguration tunerConfiguration) - { - super.setItem(tunerConfiguration); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if(hasItem()) - { - FCD1TunerConfiguration config = getConfiguration(); - - if(tunerConfiguration.isAssigned()) - { - setControlsEnabled(true); - mConfigurationName.setText(config.getName()); - mFrequencyCorrection.setValue(config.getFrequencyCorrection()); - mComboLNAEnhance.setSelectedItem(config.getLNAEnhance()); - mComboLNAGain.setSelectedItem(config.getLNAGain()); - mComboMixerGain.setSelectedItem(config.getMixerGain()); - mCorrectionDCI.setValue(config.getInphaseDCCorrection()); - mCorrectionDCQ.setValue(config.getQuadratureDCCorrection()); - mCorrectionGain.setValue(config.getGainCorrection()); - mCorrectionPhase.setValue(config.getPhaseCorrection()); - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(config.getName()); - } - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(""); - } - - mLoading = false; - } - - public enum Correction - { - GAIN, PHASE, DC_INPHASE, DC_QUADRATURE - } - - ; + public enum Correction {GAIN, PHASE, DC_INPHASE, DC_QUADRATURE}; public class CorrectionSpinner extends JSpinner { @@ -495,29 +408,27 @@ public class FCD1TunerEditor extends TunerConfigurationEditor editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - addChangeListener(new ChangeListener() + addChangeListener(e -> { - @Override - public void stateChanged(ChangeEvent e) + if(!isLoading()) { - double value = - ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); + double value = ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); try { switch(mCorrectionComponent) { case DC_INPHASE: - mController.setDCCorrectionInPhase(value); + getController().setDCCorrectionInPhase(value); break; case DC_QUADRATURE: - mController.setDCCorrectionQuadrature(value); + getController().setDCCorrectionQuadrature(value); break; case GAIN: - mController.setGainCorrection(value); + getController().setGainCorrection(value); break; case PHASE: - mController.setPhaseCorrection(value); + getController().setPhaseCorrection(value); break; } @@ -526,11 +437,10 @@ public class FCD1TunerEditor extends TunerConfigurationEditor catch(Exception e1) { JOptionPane.showMessageDialog(FCD1TunerEditor.this, "FCDPro - error " - + "applying " + mCorrectionComponent.toString() + " correction value [" - + value + "]"); + + "applying " + mCorrectionComponent.toString() + " correction value [" + value + "]"); - mLog.error("FCDPro - error applying " + mCorrectionComponent.toString() - + " correction value [" + value + "]", e1); + mLog.error("FCDPro - error applying " + mCorrectionComponent.toString() + " correction value [" + + value + "]", e1); } } }); diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java index 6d11d176..5e237ef0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java @@ -1,22 +1,24 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.fcd.proplusV2; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; @@ -25,7 +27,6 @@ public class FCD2TunerConfiguration extends TunerConfiguration { private boolean mGainLNA = true; private boolean mGainMixer = true; - private double mFrequencyCorrection = -2.2d; /** * Default constructor for JAXB @@ -34,18 +35,18 @@ public class FCD2TunerConfiguration extends TunerConfiguration { } - public FCD2TunerConfiguration(String uniqueID, String name) - { - super(uniqueID, name); - } - - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @JsonIgnore @Override public TunerType getTunerType() { return TunerType.FUNCUBE_DONGLE_PRO_PLUS; } + public FCD2TunerConfiguration(String uniqueID) + { + super(uniqueID); + } + @JacksonXmlProperty(isAttribute = true, localName = "lna_gain") public boolean getGainLNA() { @@ -67,15 +68,4 @@ public class FCD2TunerConfiguration extends TunerConfiguration { mGainMixer = gain; } - - @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection(double value) - { - mFrequencyCorrection = value; - } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java index 1a4b29b0..1a61ca29 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,11 +14,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.fcd.proplusV2; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.MixerTunerType; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; @@ -27,13 +28,14 @@ import io.github.dsheirer.source.tuner.fcd.FCDCommand; import io.github.dsheirer.source.tuner.fcd.FCDTunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; import javax.sound.sampled.TargetDataLine; import java.nio.ByteBuffer; import java.nio.ByteOrder; +/** + * Funcube Dongle Pro Plus tuner controller. Combines USB HID control with Audio Mixer for data streaming. + */ public class FCD2TunerController extends FCDTunerController { private final static Logger mLog = LoggerFactory.getLogger(FCD2TunerController.class); @@ -42,16 +44,21 @@ public class FCD2TunerController extends FCDTunerController public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000; public static final int SAMPLE_RATE = 192000; - public FCD2TunerController(TargetDataLine mixerTDL, Device device, DeviceDescriptor descriptor) + /** + * Constructs an instance + * @param mixerTDL for the sample stream + * @param bus usb + * @param port usb + * @param tunerErrorListener to receive errors from this tuner + */ + public FCD2TunerController(TargetDataLine mixerTDL, int bus, int port, ITunerErrorListener tunerErrorListener) { - super(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS, mixerTDL, device, descriptor, - MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY); + super(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS, mixerTDL, bus, port, MINIMUM_TUNABLE_FREQUENCY, + MAXIMUM_TUNABLE_FREQUENCY, tunerErrorListener); } - public void init() throws SourceException + protected void deviceStart() throws SourceException { - super.init(); - mFrequencyController.setSampleRate(SAMPLE_RATE); try @@ -83,69 +90,24 @@ public class FCD2TunerController extends FCDTunerController public void setLNAGain(boolean enabled) throws SourceException { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_LNA_GAIN, enabled ? 1 : 0); - } - catch(Exception e) - { - throw new SourceException("error while setting LNA Gain", e); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_LNA_GAIN, enabled ? 1 : 0); } public void setMixerGain(boolean enabled) throws SourceException { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_MIXER_GAIN, enabled ? 1 : 0); - } - catch(Exception e) - { - throw new SourceException("error while setting Mixer Gain", e); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_MIXER_GAIN, enabled ? 1 : 0); } @Override public void apply(TunerConfiguration config) throws SourceException { - if(config instanceof FCD2TunerConfiguration) + //Invoke super for frequency, frequency correction and autoPPM + super.apply(config); + + if(config instanceof FCD2TunerConfiguration fcd2) { - FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config; - - setFrequencyCorrection(fcd2.getFrequencyCorrection()); - - long frequency = MINIMUM_TUNABLE_FREQUENCY; - - if(fcd2.getFrequency() >= MINIMUM_TUNABLE_FREQUENCY && - fcd2.getFrequency() <= MAXIMUM_TUNABLE_FREQUENCY) - { - frequency = fcd2.getFrequency(); - } - - setFrequency(frequency); setLNAGain(fcd2.getGainLNA()); setMixerGain(fcd2.getGainMixer()); - - try - { - setFrequency(fcd2.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } } } @@ -156,9 +118,7 @@ public class FCD2TunerController extends FCDTunerController try { ByteBuffer buffer = send(FCDCommand.APP_GET_DC_CORRECTION); - buffer.order(ByteOrder.LITTLE_ENDIAN); - return buffer.getInt(2); } catch(Exception e) @@ -171,20 +131,7 @@ public class FCD2TunerController extends FCDTunerController public void setDCCorrection(int value) { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_DC_CORRECTION, value); - } - catch(Exception e) - { - mLog.error("error setting dc correction to [" + value + "]", e); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_DC_CORRECTION, value); } public int getIQCorrection() @@ -194,9 +141,7 @@ public class FCD2TunerController extends FCDTunerController try { ByteBuffer buffer = send(FCDCommand.APP_GET_IQ_CORRECTION); - buffer.order(ByteOrder.LITTLE_ENDIAN); - return buffer.getInt(2); } catch(Exception e) @@ -209,19 +154,6 @@ public class FCD2TunerController extends FCDTunerController public void setIQCorrection(int value) { - mLock.lock(); - - try - { - send(FCDCommand.APP_SET_IQ_CORRECTION, value); - } - catch(Exception e) - { - mLog.error("error setting IQ correction to [" + value + "]", e); - } - finally - { - mLock.unlock(); - } + send(FCDCommand.APP_SET_IQ_CORRECTION, value); } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java index bfebb8c6..c4b7a258 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java @@ -1,27 +1,29 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.fcd.proplusV2; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent.Event; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.fcd.FCDTuner; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,217 +33,169 @@ import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; -public class FCD2TunerEditor extends TunerConfigurationEditor +/** + * Funcube Dongle Pro Plus tuner editor + */ +public class FCD2TunerEditor extends TunerEditor { private final static Logger mLog = LoggerFactory.getLogger(FCD2TunerEditor.class); private static final long serialVersionUID = 1L; + private JButton mTunerInfoButton; + private JCheckBox mLnaGainCheckBox; + private JCheckBox mMixerGainCheckBox; - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JSpinner mFrequencyCorrection; - private JCheckBox mLNAGain; - private JCheckBox mMixerGain; - private boolean mLoading; - - private FCD2TunerController mController; - - public FCD2TunerEditor(TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner) + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager to save configuration + * @param discoveredTuner to control + */ + public FCD2TunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) { - super(tunerConfigurationModel); - mController = (FCD2TunerController)tuner.getController(); - + super(userPreferences, tunerManager, discoveredTuner); init(); + tunerStatusUpdated(); } - private FCD2TunerConfiguration getConfiguration() + private FCD2TunerController getController() { - if(hasItem()) + if(hasTuner()) { - return (FCD2TunerConfiguration)getItem(); + return (FCD2TunerController)getTuner().getController(); } return null; } + @Override + protected void tunerStatusUpdated() + { + setLoading(true); + + if(hasTuner()) + { + getTunerIdLabel().setText(getTuner().getPreferredName()); + } + else + { + getTunerIdLabel().setText(getDiscoveredTuner().getId()); + } + + String status = getDiscoveredTuner().getTunerStatus().toString(); + if(getDiscoveredTuner().hasErrorMessage()) + { + status += " - " + getDiscoveredTuner().getErrorMessage(); + } + getTunerStatusLabel().setText(status); + getButtonPanel().updateControls(); + getFrequencyPanel().updateControls(); + getTunerInfoButton().setEnabled(hasTuner()); + getLnaGainCheckBox().setEnabled(hasTuner()); + getMixerGainCheckBox().setEnabled(hasTuner()); + + if(hasTuner() && hasConfiguration()) + { + getLnaGainCheckBox().setSelected(getConfiguration().getGainLNA()); + getMixerGainCheckBox().setSelected(getConfiguration().getGainMixer()); + } + + setLoading(false); + } + private void init() { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][grow]")); + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][grow]")); - add(new JLabel("FCD Pro+ Tuner Configuration"), "span,align center"); + add(new JLabel("Tuner:")); + add(getTunerIdLabel()); + add(getTunerInfoButton()); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); + + add(getButtonPanel(), "span,align left"); + + add(new JSeparator(), "span,growx,push"); + add(new JLabel("Frequency (MHz):")); + add(getFrequencyPanel(), "wrap"); + + add(new JSeparator(), "span,growx,push"); + add(getLnaGainCheckBox()); + add(getMixerGainCheckBox()); + } + + public JButton getTunerInfoButton() + { + if(mTunerInfoButton == null) { - @Override - public void focusLost(FocusEvent e) - { - save(); - } + mTunerInfoButton = new JButton("Tuner Info"); + mTunerInfoButton.setEnabled(false); + mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(FCD2TunerEditor.this, + getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE)); + } - @Override - public void focusGained(FocusEvent e) - { - } - }); + return mTunerInfoButton; + } - add(new JLabel("Name:")); - add(mConfigurationName, "span 2"); - - mTunerInfo = new JButton("Tuner Info"); - mTunerInfo.setEnabled(false); - mTunerInfo.addActionListener(new ActionListener() + public JCheckBox getLnaGainCheckBox() + { + if(mLnaGainCheckBox == null) { - @Override - public void actionPerformed(ActionEvent e) + mLnaGainCheckBox = new JCheckBox("LNA Gain"); + mLnaGainCheckBox.setEnabled(false); + mLnaGainCheckBox.addActionListener(event -> { - JOptionPane.showMessageDialog(FCD2TunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(mTunerInfo); + if(!isLoading()) + { + try + { + getController().setLNAGain(getLnaGainCheckBox().isSelected()); + save(); + } + catch(SourceException e) + { + mLog.error("Couldn't set LNA gain for FCD2", e); + } + } + }); + } - SpinnerModel model = new SpinnerNumberModel(0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1); //step + return mLnaGainCheckBox; + } - mFrequencyCorrection = new JSpinner(model); - mFrequencyCorrection.setEnabled(false); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits(1); - editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - - mFrequencyCorrection.addChangeListener(new ChangeListener() + public JCheckBox getMixerGainCheckBox() + { + if(mMixerGainCheckBox == null) { - @Override - public void stateChanged(ChangeEvent e) + mMixerGainCheckBox = new JCheckBox("Mixer Gain"); + mMixerGainCheckBox.setEnabled(false); + mMixerGainCheckBox.addActionListener(event -> { - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try + if(!isLoading()) { - mController.setFrequencyCorrection(value); - save(); + try + { + getController().setMixerGain(getMixerGainCheckBox().isSelected()); + save(); + } + catch(SourceException e) + { + mLog.error("Couldn't set mixer gain for FCD2", e); + } } - catch(SourceException e1) - { - JOptionPane.showMessageDialog(FCD2TunerEditor.this, "FCD Pro Plus Tuner " - + "Controller - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage()); + }); + } - mLog.error("FuncubeDongleProPlus Controller - couldn't apply " - + "frequency correction value: " + value, e1); - } - } - }); - - add(new JLabel("PPM:")); - add(mFrequencyCorrection, "wrap"); - - add(new JSeparator(JSeparator.HORIZONTAL), "span,grow"); - - mLNAGain = new JCheckBox("LNA Gain"); - mLNAGain.setEnabled(false); - mLNAGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent event) - { - try - { - mController.setLNAGain(mLNAGain.isSelected()); - save(); - } - catch(SourceException e) - { - mLog.error("Couldn't set LNA gain for FCD2", e); - } - } - }); - - add(new JLabel("")); - add(mLNAGain); - - /** - * Mixer Gain - */ - mMixerGain = new JCheckBox("Mixer Gain"); - - mMixerGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent event) - { - try - { - mController.setMixerGain(mMixerGain.isSelected()); - save(); - } - catch(SourceException e) - { - mLog.error("Couldn't set mixer gain for FCD2", e); - } - } - }); - - add(new JLabel("")); - add(mMixerGain); + return mMixerGainCheckBox; } @Override public void setTunerLockState(boolean locked) { - //no-op - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled(boolean enabled) - { - if(mConfigurationName.isEnabled() != enabled) - { - mConfigurationName.setEnabled(enabled); - } - - if(mTunerInfo.isEnabled() != enabled) - { - mTunerInfo.setEnabled(enabled); - } - - if(mFrequencyCorrection.isEnabled() != enabled) - { - mFrequencyCorrection.setEnabled(enabled); - } - - if(mLNAGain.isEnabled() != enabled) - { - mLNAGain.setEnabled(enabled); - } - - if(mMixerGain.isEnabled() != enabled) - { - mMixerGain.setEnabled(enabled); - } + super.setTunerLockState(locked); } private String getTunerInfo() @@ -250,25 +204,28 @@ public class FCD2TunerEditor extends TunerConfigurationEditor sb.append("

Funcube Dongle Pro Plus Tuner

"); - sb.append("USB ID: "); - sb.append(mController.getUSBID()); - sb.append("
"); + if(hasTuner()) + { + sb.append("USB ID: "); + sb.append(getController().getUSBID()); + sb.append("
"); - sb.append("USB Address: "); - sb.append(mController.getUSBAddress()); - sb.append("
"); + sb.append("USB Address: "); + sb.append(getController().getUSBAddress()); + sb.append("
"); - sb.append("USB Speed: "); - sb.append(mController.getUSBSpeed()); - sb.append("
"); + sb.append("USB Speed: "); + sb.append(getController().getUSBSpeed()); + sb.append("
"); - sb.append("Cellular Band: "); - sb.append(mController.getConfiguration().getBandBlocking()); - sb.append("
"); + sb.append("Cellular Band: "); + sb.append(getController().getConfiguration().getBandBlocking()); + sb.append("
"); - sb.append("Firmware: "); - sb.append(mController.getConfiguration().getFirmware()); - sb.append("
"); + sb.append("Firmware: "); + sb.append(getController().getConfiguration().getFirmware()); + sb.append("
"); + } return sb.toString(); } @@ -276,57 +233,15 @@ public class FCD2TunerEditor extends TunerConfigurationEditor @Override public void save() { - if(hasItem() && !mLoading) + if(hasConfiguration() && !isLoading()) { - FCD2TunerConfiguration config = getConfiguration(); - - config.setName(mConfigurationName.getText()); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection(value); - - config.setGainLNA(mLNAGain.isSelected()); - config.setGainMixer(mMixerGain.isSelected()); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent(getConfiguration(), Event.CHANGE)); + getConfiguration().setFrequency(getFrequencyControl().getFrequency()); + double value = ((SpinnerNumberModel) getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); + getConfiguration().setFrequencyCorrection(value); + getConfiguration().setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected()); + getConfiguration().setGainLNA(getLnaGainCheckBox().isSelected()); + getConfiguration().setGainMixer(getMixerGainCheckBox().isSelected()); + saveConfiguration(); } } - - @Override - public void setItem(TunerConfiguration tunerConfiguration) - { - super.setItem(tunerConfiguration); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if(hasItem()) - { - FCD2TunerConfiguration config = getConfiguration(); - - - if(tunerConfiguration.isAssigned()) - { - setControlsEnabled(true); - mConfigurationName.setText(config.getName()); - mFrequencyCorrection.setValue(config.getFrequencyCorrection()); - mLNAGain.setSelected(config.getGainLNA()); - mMixerGain.setSelected(config.getGainMixer()); - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(config.getName()); - } - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(""); - } - - mLoading = false; - } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java index 9da7037b..93b829c1 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This 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 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.source.tuner.frequency; @@ -36,20 +35,21 @@ public class FrequencyController private Tunable mTunable; private long mFrequency = 101100000; private long mTunedFrequency = 101100000; - private long mMinimumFrequency; - private long mMaximumFrequency; + private long mMinimumFrequency = 0; + private long mMaximumFrequency = 0; private double mFrequencyCorrection = 0.0d; private double mSampleRate = 0.0d; private boolean mLocked = false; private List mProcessors = new CopyOnWriteArrayList<>(); - public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection) + /** + * Constructs an instance + * @param tunable that can be controlled by this frequency controller. + */ + public FrequencyController(Tunable tunable) { mTunable = tunable; - mMinimumFrequency = minFrequency; - mMaximumFrequency = maxFrequency; - mFrequencyCorrection = frequencyCorrection; } /** @@ -193,16 +193,42 @@ public class FrequencyController return mTunedFrequency; } + /** + * Minimum tunable frequency + * @return minimum in Hertz + */ public long getMinimumFrequency() { return mMinimumFrequency; } + /** + * Sets the minimum tunable frequency + * @param minimum in Hertz + */ + public void setMinimumFrequency(long minimum) + { + mMaximumFrequency = minimum; + } + + /** + * Maximum tunable frequency + * @return maximum frequency in Hertz + */ public long getMaximumFrequency() { return mMaximumFrequency; } + /** + * Sets the maximum tunable frequency + * @param maximum in Hertz + */ + public void setMaximumFrequency(long maximum) + { + mMaximumFrequency = maximum; + } + /** * Calculate the tuned frequency by adding frequency correction to the * corrected frequency. @@ -304,21 +330,21 @@ public class FrequencyController /** * Gets the tuned frequency of the device */ - public long getTunedFrequency() throws SourceException; + long getTunedFrequency() throws SourceException; /** * Sets the tuned frequency of the device */ - public void setTunedFrequency(long frequency) throws SourceException; + void setTunedFrequency(long frequency) throws SourceException; /** * Gets the current bandwidth setting of the device */ - public double getCurrentSampleRate() throws SourceException; + double getCurrentSampleRate() throws SourceException; /** * Indicates if this tunable can tune the frequency */ - public boolean canTune(long frequency); + boolean canTune(long frequency); } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java index 224cbcbe..f55dbff6 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java @@ -1,42 +1,64 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.hackrf; -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.preference.source.ChannelizerType; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.BoardID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * HackRF Tuner + */ public class HackRFTuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger(HackRFTuner.class); - public HackRFTuner(HackRFTunerController controller, UserPreferences userPreferences) throws SourceException + /** + * Constructs an instance + * @param controller for the HackRF + * @param tunerErrorListener to listen for errors from this tuner + * @param channelizerType for the channelizer + */ + public HackRFTuner(HackRFTunerController controller, ITunerErrorListener tunerErrorListener, + ChannelizerType channelizerType) { - super("HackRF", controller, userPreferences); + super(controller, tunerErrorListener, channelizerType); + } + + @Override + public String getPreferredName() + { + try + { + HackRFTunerController.Serial serial = getController().getSerial(); + return "HackRF " + getController().getTunerType() + " " + serial.getSerialNumber(); + } + catch(Exception e) + { + //No - op + } + + return "HackRF " + getController().getTunerType(); } public HackRFTunerController getController() @@ -47,13 +69,7 @@ public class HackRFTuner extends Tuner @Override public TunerClass getTunerClass() { - return TunerClass.HACKRF_ONE; - } - - @Override - public TunerType getTunerType() - { - return TunerType.HACKRF; + return TunerClass.HACKRF; } @Override @@ -67,7 +83,7 @@ public class HackRFTuner extends Tuner { try { - return getController().getSerial().getSerialNumber(); + return getTunerClass().toString() + " " + getController().getSerial().getSerialNumber(); } catch(Exception e) { diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerConfiguration.java index 75e163f0..a9e328e5 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerConfiguration.java @@ -1,23 +1,25 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.hackrf; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; @@ -31,7 +33,6 @@ public class HackRFTunerConfiguration extends TunerConfiguration private HackRFLNAGain mLNAGain = HackRFLNAGain.GAIN_16; // We can see some signal at this gain private HackRFVGAGain mVGAGain = HackRFVGAGain.GAIN_16; // We can see some signal at this gain private boolean mAmplifierEnabled = false; //Probably should start off disabled - private double mFrequencyCorrection = 0.0d; /** * Default constructor for JAXB @@ -40,16 +41,16 @@ public class HackRFTunerConfiguration extends TunerConfiguration { } - public HackRFTunerConfiguration(String uniqueID, String name) + public HackRFTunerConfiguration(String uniqueID) { - super(uniqueID, name); + super(uniqueID); } - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @JsonIgnore @Override public TunerType getTunerType() { - return TunerType.HACKRF; + return TunerType.HACKRF_ONE; } @JacksonXmlProperty(isAttribute = true, localName = "amplifier_enabled") @@ -85,17 +86,6 @@ public class HackRFTunerConfiguration extends TunerConfiguration mVGAGain = vgaGain; } - @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection(double value) - { - mFrequencyCorrection = value; - } - @JacksonXmlProperty(isAttribute = true, localName = "sample_rate") public HackRFSampleRate getSampleRate() { diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java index 644550f5..1e3d78b0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java @@ -18,17 +18,16 @@ */ package io.github.dsheirer.source.tuner.hackrf; +import io.github.dsheirer.buffer.INativeBufferFactory; import io.github.dsheirer.buffer.SignedByteNativeBufferFactory; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; +import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.usb.USBTransferProcessor; import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.apache.commons.io.EndianUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; @@ -37,43 +36,79 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.EnumSet; -import java.util.concurrent.locks.ReentrantLock; public class HackRFTunerController extends USBTunerController { private final static Logger mLog = LoggerFactory.getLogger(HackRFTunerController.class); public final static long USB_TIMEOUT_US = 1000000l; //uSeconds - public static final byte USB_ENDPOINT = (byte)0x81; - public static final byte USB_INTERFACE = (byte)0x0; public static final int USB_TRANSFER_BUFFER_SIZE = 262144; public static final byte REQUEST_TYPE_IN = (byte)(LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); public static final byte REQUEST_TYPE_OUT = (byte)(LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); - public static final long MIN_FREQUENCY = 10000000l; - public static final long MAX_FREQUENCY = 6000000000l; + public static final long MINIMUM_TUNABLE_FREQUENCY = 10000000l; + public static final long MAXIMUM_TUNABLE_FREQUENCY = 6000000000l; public static final long DEFAULT_FREQUENCY = 101100000; - public static final double USABLE_BANDWIDTH_PERCENT = 0.90; - public static final int DC_SPIKE_AVOID_BUFFER = 5000; - - private SignedByteNativeBufferFactory mNativeBufferFactory = new SignedByteNativeBufferFactory(); - private USBTransferProcessor mUSBTransferProcessor; + public static final double USABLE_BANDWIDTH = 0.90; + public static final int DC_HALF_BANDWIDTH = 5000; + private INativeBufferFactory mNativeBufferFactory = new SignedByteNativeBufferFactory(); private HackRFSampleRate mSampleRate = HackRFSampleRate.RATE_5_0; private boolean mAmplifierEnabled = false; - private Device mDevice; - private DeviceDescriptor mDeviceDescriptor; - private DeviceHandle mDeviceHandle; - private ReentrantLock mLock = new ReentrantLock(); - - public HackRFTunerController(Device device, DeviceDescriptor descriptor) throws SourceException + /** + * Constructs an instance + * @param bus usb + * @param port usb + */ + public HackRFTunerController(int bus, int port, ITunerErrorListener tunerErrorListener) { - super(MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); + super(bus, port, MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY, DC_HALF_BANDWIDTH, USABLE_BANDWIDTH, + tunerErrorListener); + } - mDevice = device; - mDeviceDescriptor = descriptor; + /** + * Tuner type for this HackRf + */ + @Override + public TunerType getTunerType() + { + if(isRunning()) + { + try + { + BoardID boardID = getBoardID(); + + switch(boardID) + { + case RAD1O: + return TunerType.HACKRF_RAD1O; + case JAWBREAKER: + return TunerType.HACKRF_JAWBREAKER; + case HACKRF_ONE: + return TunerType.HACKRF_ONE; + }; + } + catch(UsbException ue) + { + mLog.error("Couldn't read HackRF board ID - " + ue.getLocalizedMessage()); + } + } + + return TunerType.HACKRF_ONE; + } + + @Override + protected INativeBufferFactory getNativeBufferFactory() + { + return mNativeBufferFactory; + } + + @Override + protected int getTransferBufferSize() + { + return USB_TRANSFER_BUFFER_SIZE; } @Override @@ -82,118 +117,37 @@ public class HackRFTunerController extends USBTunerController return USB_TRANSFER_BUFFER_SIZE / 2; } - public void init() throws SourceException + /** + * Device startup actions invoked by the parent start() method. + * @throws SourceException if there is an error performing these operations + */ + protected void deviceStart() throws SourceException { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != 0) - { - if(result == LibUsb.ERROR_ACCESS) - { - mLog.error("Unable to access HackRF - insufficient permissions. " - + "If you are running a Linux OS, have you installed the " - + "hackRF rules file in \\etc\\udev\\rules.d ??"); - } - - throw new SourceException("Couldn't open hackrf device - " + - LibUsb.strError(result)); - } - try { - claimInterface(); - setMode(Mode.RECEIVE); - setFrequency(DEFAULT_FREQUENCY); } catch(Exception e) { - throw new SourceException("HackRF Tuner Controller - couldn't " - + "claim USB interface or get endpoint or pipe", e); + mLog.error("Error on Hack RF startup", e); + throw new SourceException("HackRF Tuner Controller - couldn't claim USB interface or get endpoint or pipe", e); } - - String name; - - try - { - name = "HackRF " + getSerial().getSerialNumber(); - } - catch(UsbException ue) - { - //Do nothing, we couldn't determine the serial number - name = "HackRF - Unidentified Serial"; - } - - mUSBTransferProcessor = new USBTransferProcessor(name, mDeviceHandle, mNativeBufferFactory, - USB_TRANSFER_BUFFER_SIZE, this); - } - - @Override - public void dispose() - { - if(mDeviceHandle != null) - { - mLog.info("Releasing HackRF Tuner"); - try - { - LibUsb.close(mDeviceHandle); - } - catch(Exception e) - { - mLog.error("error while closing device handle", e); - } - - mDeviceHandle = null; - } - } - - @Override - protected USBTransferProcessor getUSBTransferProcessor() - { - return mUSBTransferProcessor; } /** - * Claims the USB interface. If another application currently has - * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting - * will dictate if the interface is forcibly claimed from the other - * application + * Devices shutdown actions invoked by the parent stop() method */ - private void claimInterface() throws SourceException + @Override + protected void deviceStop() { - if(mDeviceHandle != null) + try { - int result = LibUsb.kernelDriverActive(mDeviceHandle, USB_INTERFACE); - - if(result == 1) - { - result = LibUsb.detachKernelDriver(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + - LibUsb.errorName(result) + "]"); - - throw new SourceException("couldn't detach kernel driver " - + "from device"); - } - } - - result = LibUsb.claimInterface(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + - LibUsb.errorName(result) + "]"); - } + setMode(Mode.OFF); } - else + catch(UsbException ue) { - throw new SourceException("couldn't claim usb interface - no " - + "device handle"); + mLog.error("Error while setting HackRF mode to OFF", ue); } } @@ -203,7 +157,6 @@ public class HackRFTunerController extends USBTunerController public BoardID getBoardID() throws UsbException { int id = readByte(Request.BOARD_ID_READ, (byte)0, (byte)0, false); - return BoardID.lookup(id); } @@ -213,11 +166,8 @@ public class HackRFTunerController extends USBTunerController public String getFirmwareVersion() throws UsbException { ByteBuffer buffer = readArray(Request.VERSION_STRING_READ, 0, 0, 255); - byte[] data = new byte[255]; - buffer.get(data); - return new String(data); } @@ -227,7 +177,6 @@ public class HackRFTunerController extends USBTunerController public Serial getSerial() throws UsbException { ByteBuffer buffer = readArray(Request.BOARD_PARTID_SERIALNO_READ, 0, 0, 24); - return new Serial(buffer); } @@ -242,18 +191,9 @@ public class HackRFTunerController extends USBTunerController /** * Sets the HackRF baseband filter */ - public void setBasebandFilter(BasebandFilter filter) throws UsbException + public void setBasebandFilter(BasebandFilter filter) throws UsbException { - mLock.lock(); - - try - { - write(Request.BASEBAND_FILTER_BANDWIDTH_SET, filter.getLowValue(), filter.getHighValue()); - } - finally - { - mLock.unlock(); - } + write(Request.BASEBAND_FILTER_BANDWIDTH_SET, filter.getLowValue(), filter.getHighValue()); } /** @@ -261,17 +201,8 @@ public class HackRFTunerController extends USBTunerController */ public void setAmplifierEnabled(boolean enabled) throws UsbException { - mLock.lock(); - - try - { - write(Request.AMP_ENABLE, (enabled ? 1 : 0), 0); - mAmplifierEnabled = enabled; - } - finally - { - mLock.unlock();; - } + write(Request.AMP_ENABLE, (enabled ? 1 : 0), 0); + mAmplifierEnabled = enabled; } public boolean getAmplifier() @@ -284,20 +215,11 @@ public class HackRFTunerController extends USBTunerController */ public void setLNAGain(HackRFLNAGain gain) throws UsbException { - mLock.lock(); + int result = readByte(Request.SET_LNA_GAIN, 0, gain.getValue(), true); - try + if(result != 1) { - int result = readByte(Request.SET_LNA_GAIN, 0, gain.getValue(), true); - - if(result != 1) - { - throw new UsbException("couldn't set lna gain to " + gain); - } - } - finally - { - mLock.unlock(); + throw new UsbException("couldn't set lna gain to " + gain); } } @@ -306,20 +228,11 @@ public class HackRFTunerController extends USBTunerController */ public void setVGAGain(HackRFVGAGain gain) throws UsbException { - mLock.lock(); + int result = readByte(Request.SET_VGA_GAIN, 0, gain.getValue(), true); - try + if(result != 1) { - int result = readByte(Request.SET_VGA_GAIN, 0, gain.getValue(), true); - - if(result != 1) - { - throw new UsbException("couldn't set vga gain to " + gain); - } - } - finally - { - mLock.unlock(); + throw new UsbException("couldn't set vga gain to " + gain); } } @@ -334,34 +247,25 @@ public class HackRFTunerController extends USBTunerController @Override public void setTunedFrequency(long frequency) throws SourceException { - mLock.lock(); + ByteBuffer buffer = ByteBuffer.allocateDirect(8); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int mhz = (int)(frequency / 1E6); + int hz = (int)(frequency - (mhz * 1E6)); + + buffer.putInt(mhz); + buffer.putInt(hz); + + buffer.rewind(); try { - ByteBuffer buffer = ByteBuffer.allocateDirect(8); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int mhz = (int)(frequency / 1E6); - int hz = (int)(frequency - (mhz * 1E6)); - - buffer.putInt(mhz); - buffer.putInt(hz); - - buffer.rewind(); - - try - { - write(Request.SET_FREQUENCY, 0, 0, buffer); - } - catch(UsbException e) - { - mLog.error("error setting frequency [" + frequency + "]", e); - throw new SourceException("error setting frequency [" + frequency + "]", e); - } + write(Request.SET_FREQUENCY, 0, 0, buffer); } - finally + catch(UsbException e) { - mLock.unlock(); + mLog.error("error setting frequency [" + frequency + "]", e); + throw new SourceException("error setting frequency [" + frequency + "]", e); } } @@ -374,39 +278,30 @@ public class HackRFTunerController extends USBTunerController @Override public void apply(TunerConfiguration config) throws SourceException { - if(config instanceof HackRFTunerConfiguration) - { - HackRFTunerConfiguration hackRFConfig = (HackRFTunerConfiguration)config; + //Invoke super for frequency, frequency correction and autoPPM + super.apply(config); + if(config instanceof HackRFTunerConfiguration hackRFConfig) + { //Convert legacy sample rate setting to new sample rates if(!hackRFConfig.getSampleRate().isValidSampleRate()) { - mLog.warn("Changing legacy HackRF Sample Rates Setting [" + hackRFConfig.getSampleRate().name() + "] to current valid setting"); + mLog.warn("Changing legacy HackRF Sample Rates Setting [" + hackRFConfig.getSampleRate().name() + + "] to current valid setting"); hackRFConfig.setSampleRate(HackRFSampleRate.RATE_5_0); } try { setSampleRate(hackRFConfig.getSampleRate()); - setFrequencyCorrection(hackRFConfig.getFrequencyCorrection()); setAmplifierEnabled(hackRFConfig.getAmplifierEnabled()); setLNAGain(hackRFConfig.getLNAGain()); setVGAGain(hackRFConfig.getVGAGain()); - setFrequency(getFrequency()); } catch(UsbException e) { throw new SourceException("Error while applying tuner configuration", e); } - - try - { - setFrequency(hackRFConfig.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } } else { @@ -414,39 +309,22 @@ public class HackRFTunerController extends USBTunerController } } - public ByteBuffer readArray(Request request, - int value, - int index, - int length) throws UsbException + public ByteBuffer readArray(Request request, int value, int index, int length) throws UsbException { - if(mDeviceHandle != null) + ByteBuffer buffer = ByteBuffer.allocateDirect(length); + + int transferred = LibUsb.controlTransfer(getDeviceHandle(), REQUEST_TYPE_IN, request.getRequestNumber(), + (short)value, (short)index, buffer, USB_TIMEOUT_US); + + if(transferred < 0) { - ByteBuffer buffer = ByteBuffer.allocateDirect(length); - - int transferred = LibUsb.controlTransfer(mDeviceHandle, - REQUEST_TYPE_IN, - request.getRequestNumber(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_US); - - if(transferred < 0) - { - throw new LibUsbException("read error", transferred); - } - - return buffer; - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); + throw new LibUsbException("read error", transferred); } + + return buffer; } - public int read(Request request, int value, int index, int length) - throws UsbException + public int read(Request request, int value, int index, int length) throws UsbException { if(!(length == 1 || length == 2 || length == 4)) { @@ -474,61 +352,36 @@ public class HackRFTunerController extends USBTunerController } } - public int readByte(Request request, int value, int index, boolean signed) - throws UsbException + public synchronized int readByte(Request request, int value, int index, boolean signed) throws UsbException { ByteBuffer buffer = readArray(request, value, index, 1); if(signed) { - return (int)(buffer.get()); + return buffer.get(); } else { - return (int)(buffer.get() & 0xFF); + return (buffer.get() & 0xFF); } } - public void write(Request request, - int value, - int index, - ByteBuffer buffer) throws UsbException + public synchronized void write(Request request, int value, int index, ByteBuffer buffer) throws UsbException { - if(mDeviceHandle != null) - { - int transferred = LibUsb.controlTransfer(mDeviceHandle, - REQUEST_TYPE_OUT, - request.getRequestNumber(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_US); + int status = LibUsb.controlTransfer(getDeviceHandle(), REQUEST_TYPE_OUT, request.getRequestNumber(), + (short)value, (short)index, buffer, USB_TIMEOUT_US); - if(transferred < 0) - { - throw new LibUsbException("error writing byte buffer", - transferred); - } - else if(transferred != buffer.capacity()) - { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); - } - } - else + if(status < LibUsb.SUCCESS) { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); + throw new LibUsbException("error writing control transfer for request [" + request.name() + + "] value [" + value + "]", status); } } /** * Sends a request that doesn't have a data payload */ - public void write(Request request, - int value, - int index) throws UsbException + public void write(Request request, int value, int index) throws UsbException { write(request, value, index, ByteBuffer.allocateDirect(0)); } @@ -548,24 +401,17 @@ public class HackRFTunerController extends USBTunerController } setSampleRateManual(rate.getRate(), 1); - mFrequencyController.setSampleRate(rate.getRate()); - setBasebandFilter(rate.getFilter()); - mSampleRate = rate; } - public void setSampleRateManual(int frequency, int divider) - throws UsbException + public void setSampleRateManual(int frequency, int divider) throws UsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(8); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.putInt(frequency); buffer.putInt(divider); - write(Request.SET_SAMPLE_RATE, 0, 0, buffer); } @@ -601,7 +447,7 @@ public class HackRFTunerController extends USBTunerController private byte mRequestNumber; - private Request(int number) + Request(int number) { mRequestNumber = (byte)number; } @@ -655,7 +501,7 @@ public class HackRFTunerController extends USBTunerController private String mLabel; private BasebandFilter mFilter; - private HackRFSampleRate(int rate, String label, BasebandFilter filter) + HackRFSampleRate(int rate, String label, BasebandFilter filter) { mRate = rate; mLabel = label; @@ -713,7 +559,7 @@ public class HackRFTunerController extends USBTunerController private int mBandwidth; private String mLabel; - private BasebandFilter(int bandwidth, String label) + BasebandFilter(int bandwidth, String label) { mBandwidth = bandwidth; mLabel = label; @@ -745,12 +591,13 @@ public class HackRFTunerController extends USBTunerController JELLYBEAN(0x00, "HackRF Jelly Bean"), JAWBREAKER(0x01, "HackRF Jaw Breaker"), HACKRF_ONE(0x02, "HackRF One"), + RAD1O(0x03, "RAD1O"), INVALID(0xFF, "HackRF Unknown Board"); private byte mIDNumber; private String mLabel; - private BoardID(int number, String label) + BoardID(int number, String label) { mIDNumber = (byte)number; mLabel = label; @@ -781,6 +628,8 @@ public class HackRFTunerController extends USBTunerController return JAWBREAKER; case 2: return HACKRF_ONE; + case 3: + return RAD1O; default: return INVALID; } @@ -797,7 +646,7 @@ public class HackRFTunerController extends USBTunerController private byte mNumber; private String mLabel; - private Mode(int number, String label) + Mode(int number, String label) { mNumber = (byte)number; mLabel = label; @@ -830,7 +679,7 @@ public class HackRFTunerController extends USBTunerController private int mValue; - private HackRFLNAGain(int value) + HackRFLNAGain(int value) { mValue = value; } @@ -842,7 +691,7 @@ public class HackRFTunerController extends USBTunerController public String toString() { - return String.valueOf(mValue) + " dB"; + return mValue + " dB"; } } @@ -886,7 +735,7 @@ public class HackRFTunerController extends USBTunerController private int mValue; - private HackRFVGAGain(int value) + HackRFVGAGain(int value) { mValue = value; } @@ -898,7 +747,7 @@ public class HackRFTunerController extends USBTunerController public String toString() { - return String.valueOf(mValue) + " dB"; + return mValue + " dB"; } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java index 629bf648..dc17ca0b 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java @@ -1,28 +1,31 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.hackrf; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,259 +35,254 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; import javax.swing.JToggleButton; -import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import javax.usb.UsbException; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; -import java.util.Collections; -public class HackRFTunerEditor extends TunerConfigurationEditor +/** + * HackRF Tuner Editor + */ +public class HackRFTunerEditor extends TunerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(HackRFTunerEditor.class); - - private JTextField mConfigurationName; private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; + private JComboBox mSampleRateCombo; private JToggleButton mAmplifier; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - private boolean mLoading; + private JComboBox mLnaGainCombo; + private JComboBox mVgaGainCombo; - private HackRFTunerController mController; - - public HackRFTunerEditor(TunerConfigurationModel tunerConfigurationModel, HackRFTuner tuner) + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager to save configuration + * @param discoveredTuner to control + */ + public HackRFTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) { - super(tunerConfigurationModel); - - mController = tuner.getController(); - + super(userPreferences, tunerManager, discoveredTuner); init(); - } - - private HackRFTunerConfiguration getConfiguration() - { - if(hasItem()) - { - return (HackRFTunerConfiguration)getItem(); - } - - return null; + tunerStatusUpdated(); } private void init() { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][grow]")); + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][][grow]")); - add(new JLabel("HackRF Tuner Configuration"), "span,align center"); + add(new JLabel("Tuner:")); + add(getTunerIdLabel()); + add(getTunerInfoButton()); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() - { - @Override - public void focusLost(FocusEvent e) - { - save(); - } + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); - @Override - public void focusGained(FocusEvent e) - { - } - }); + add(getButtonPanel(), "span,align left"); - add(new JLabel("Name:")); - add(mConfigurationName, "span 2"); + add(new JSeparator(), "span,growx,push"); - mTunerInfo = new JButton("Tuner Info"); - mTunerInfo.setEnabled(false); - mTunerInfo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - JOptionPane.showMessageDialog(HackRFTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(mTunerInfo); + add(new JLabel("Frequency (MHz):")); + add(getFrequencyPanel(), "wrap"); - - HackRFSampleRate[] validRates = HackRFSampleRate.VALID_SAMPLE_RATES - .toArray(new HackRFSampleRate[0]); - mComboSampleRate = new JComboBox<>(validRates); - mComboSampleRate.setEnabled(false); - mComboSampleRate.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - HackRFSampleRate sampleRate = (HackRFSampleRate)mComboSampleRate.getSelectedItem(); - - try - { - - mController.setSampleRate(sampleRate); - save(); - } - catch(SourceException | UsbException e2) - { - JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the sample rate setting [" + sampleRate.getLabel() + - "] " + e2.getLocalizedMessage()); - - mLog.error("HackRF Tuner Controller - couldn't apply sample rate setting [" + - sampleRate.getLabel() + "]", e); - } - } - }); add(new JLabel("Sample Rate:")); - add(mComboSampleRate); + add(getSampleRateCombo(), "wrap"); - SpinnerModel model = new SpinnerNumberModel(0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1); //step + add(new JSeparator(), "span,growx,push"); - mFrequencyCorrection = new JSpinner(model); - mFrequencyCorrection.setEnabled(false); - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + add(new JLabel("Gain Control")); + add(getAmplifierToggle(), "wrap"); - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits(1); - editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - - mFrequencyCorrection.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection(value); - save(); - } - catch(SourceException e1) - { - JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage()); - - mLog.error("HackRF Tuner Controller - couldn't apply frequency correction " - + "value: " + value, e1); - } - } - }); - - add(new JLabel("PPM:")); - add(mFrequencyCorrection); - - add(new JSeparator(JSeparator.HORIZONTAL), "span,grow"); - add(new JLabel("Gain")); - add(new JLabel(""), "span 2"); //filler - - mAmplifier = new JToggleButton("Amplifier"); - mAmplifier.setEnabled(false); - mAmplifier.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - mController.setAmplifierEnabled(mAmplifier.isSelected()); - save(); - } - catch(UsbException e) - { - mLog.error("couldn't enable/disable amplifier", e); - - JOptionPane.showMessageDialog(HackRFTunerEditor.this, "Couldn't change amplifier setting", - "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE); - } - } - }); - add(mAmplifier); - - mComboLNAGain = new JComboBox(HackRFLNAGain.values()); - mComboLNAGain.setEnabled(false); - mComboLNAGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - HackRFLNAGain lnaGain = (HackRFLNAGain)mComboLNAGain.getSelectedItem(); - - if(lnaGain == null) - { - lnaGain = HackRFLNAGain.GAIN_16; - } - - mController.setLNAGain(lnaGain); - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the LNA gain setting - " + e.getLocalizedMessage()); - - mLog.error("HackRF Tuner Controller - couldn't apply LNA gain setting - ", e); - } - } - }); - mComboLNAGain.setToolTipText("LNA Gain. Adjust to set the IF gain"); add(new JLabel("LNA:")); - add(mComboLNAGain); + add(getLnaGainCombo(), "wrap"); - mComboVGAGain = new JComboBox(HackRFVGAGain.values()); - mComboVGAGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); - - if(vgaGain == null) - { - vgaGain = HackRFVGAGain.GAIN_16; - } - - mController.setVGAGain(vgaGain); - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the VGA gain setting - " + e.getLocalizedMessage()); - - mLog.error("HackRF Tuner Controller - couldn't apply VGA gain setting", e); - } - } - }); - mComboVGAGain.setToolTipText("VGA Gain. Adjust to set the baseband gain"); add(new JLabel("VGA:")); - add(mComboVGAGain); + add(getVgaGainCombo(), "wrap"); + } + + @Override + protected void tunerStatusUpdated() + { + setLoading(true); + if(hasTuner()) + { + getTunerIdLabel().setText(getTuner().getPreferredName()); + } + else + { + getTunerIdLabel().setText(getDiscoveredTuner().getId()); + } + + String status = getDiscoveredTuner().getTunerStatus().toString(); + if(getDiscoveredTuner().hasErrorMessage()) + { + status += " - " + getDiscoveredTuner().getErrorMessage(); + } + getTunerStatusLabel().setText(status); + getButtonPanel().updateControls(); + getFrequencyPanel().updateControls(); + getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked()); + getSampleRateCombo().setSelectedItem(getConfiguration().getSampleRate()); + updateSampleRateToolTip(); + getTunerInfoButton().setEnabled(hasTuner()); + + getAmplifierToggle().setEnabled(hasTuner()); + getAmplifierToggle().setSelected(getConfiguration().getAmplifierEnabled()); + getLnaGainCombo().setEnabled(hasTuner()); + getLnaGainCombo().setSelectedItem(getConfiguration().getLNAGain()); + getVgaGainCombo().setEnabled(hasTuner()); + getVgaGainCombo().setSelectedItem(getConfiguration().getVGAGain()); + setLoading(false); + } + + private JComboBox getVgaGainCombo() + { + if(mVgaGainCombo == null) + { + mVgaGainCombo = new JComboBox(HackRFVGAGain.values()); + mVgaGainCombo.setToolTipText("VGA Gain. Adjust to set the baseband gain"); + mVgaGainCombo.setEnabled(false); + mVgaGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + HackRFVGAGain vgaGain = (HackRFVGAGain) mVgaGainCombo.getSelectedItem(); + + if(vgaGain == null) + { + vgaGain = HackRFVGAGain.GAIN_16; + } + + getTuner().getController().setVGAGain(vgaGain); + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the VGA gain setting - " + e.getLocalizedMessage()); + + mLog.error("HackRF Tuner Controller - couldn't apply VGA gain setting", e); + } + } + }); + } + + return mVgaGainCombo; + } + + private JComboBox getLnaGainCombo() + { + if(mLnaGainCombo == null) + { + mLnaGainCombo = new JComboBox(HackRFLNAGain.values()); + mLnaGainCombo.setToolTipText("LNA Gain. Adjust to set the IF gain"); + mLnaGainCombo.setEnabled(false); + mLnaGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + HackRFLNAGain lnaGain = (HackRFLNAGain) mLnaGainCombo.getSelectedItem(); + + if(lnaGain == null) + { + lnaGain = HackRFLNAGain.GAIN_16; + } + + getTuner().getController().setLNAGain(lnaGain); + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the LNA gain setting - " + e.getLocalizedMessage()); + mLog.error("HackRF Tuner Controller - couldn't apply LNA gain setting - ", e); + } + } + }); + } + + return mLnaGainCombo; + } + + private JToggleButton getAmplifierToggle() + { + if(mAmplifier == null) + { + mAmplifier = new JToggleButton("Amplifier"); + mAmplifier.setToolTipText("Enable or disable the gain amplifier"); + mAmplifier.setEnabled(false); + mAmplifier.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + getTuner().getController().setAmplifierEnabled(mAmplifier.isSelected()); + save(); + } + catch(UsbException e) + { + mLog.error("couldn't enable/disable amplifier", e); + + JOptionPane.showMessageDialog(HackRFTunerEditor.this, "Couldn't change amplifier setting", + "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE); + } + } + }); + } + + return mAmplifier; + } + + private JComboBox getSampleRateCombo() + { + if(mSampleRateCombo == null) + { + HackRFSampleRate[] validRates = HackRFSampleRate.VALID_SAMPLE_RATES.toArray(new HackRFSampleRate[0]); + mSampleRateCombo = new JComboBox<>(validRates); + mSampleRateCombo.setEnabled(false); + mSampleRateCombo.addActionListener(e -> + { + if(!isLoading()) + { + HackRFSampleRate sampleRate = (HackRFSampleRate)getSampleRateCombo().getSelectedItem(); + + try + { + getTuner().getController().setSampleRate(sampleRate); + save(); + } + catch(SourceException | UsbException e2) + { + JOptionPane.showMessageDialog(HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the sample rate setting [" + sampleRate.getLabel() + + "] " + e2.getLocalizedMessage()); + + mLog.error("HackRF Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", e); + } + } + }); + } + + return mSampleRateCombo; + } + + private JButton getTunerInfoButton() + { + if(mTunerInfo == null) + { + mTunerInfo = new JButton("Tuner Info"); + mTunerInfo.setToolTipText("Provides details and information about the tuner"); + mTunerInfo.setEnabled(false); + mTunerInfo.addActionListener(e -> JOptionPane.showMessageDialog(HackRFTunerEditor.this, + getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE)); + } + + return mTunerInfo; } /** @@ -292,67 +290,21 @@ public class HackRFTunerEditor extends TunerConfigurationEditor */ private void updateSampleRateToolTip() { - if(mController.isLocked()) + if(hasTuner() && getTuner().getController().isLocked()) { - mComboSampleRate.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); } else { - mComboSampleRate.setToolTipText("Select a sample rate for the tuner"); + mSampleRateCombo.setToolTipText("Select a sample rate for the tuner"); } } @Override public void setTunerLockState(boolean locked) { - mComboSampleRate.setEnabled(!locked); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled(boolean enabled) - { - if(mConfigurationName.isEnabled() != enabled) - { - mConfigurationName.setEnabled(enabled); - } - - if(mTunerInfo.isEnabled() != enabled) - { - mTunerInfo.setEnabled(enabled); - } - - if(mFrequencyCorrection.isEnabled() != enabled) - { - mFrequencyCorrection.setEnabled(enabled); - } - - updateSampleRateToolTip(); - - if(mController.isLocked()) - { - mComboSampleRate.setEnabled(false); - } - else if(mComboSampleRate.isEnabled() != enabled) - { - mComboSampleRate.setEnabled(enabled); - } - - if(mAmplifier.isEnabled() != enabled) - { - mAmplifier.setEnabled(enabled); - } - - if(mComboLNAGain.isEnabled() != enabled) - { - mComboLNAGain.setEnabled(enabled); - } - - if(mComboVGAGain.isEnabled() != enabled) - { - mComboVGAGain.setEnabled(enabled); - } + super.setTunerLockState(locked); + mSampleRateCombo.setEnabled(!locked); } private String getTunerInfo() @@ -361,7 +313,10 @@ public class HackRFTunerEditor extends TunerConfigurationEditor try { - board = mController.getBoardID(); + if(hasTuner()) + { + board = getTuner().getController().getBoardID(); + } } catch(UsbException e) { @@ -371,7 +326,6 @@ public class HackRFTunerEditor extends TunerConfigurationEditor StringBuilder sb = new StringBuilder(); sb.append("

HackRF Tuner

"); - sb.append("Board: "); sb.append(board.getLabel()); sb.append("
"); @@ -380,7 +334,10 @@ public class HackRFTunerEditor extends TunerConfigurationEditor try { - serial = mController.getSerial(); + if(hasTuner()) + { + serial = getTuner().getController().getSerial(); + } } catch(Exception e) { @@ -407,7 +364,10 @@ public class HackRFTunerEditor extends TunerConfigurationEditor try { - firmware = mController.getFirmwareVersion(); + if(hasTuner()) + { + firmware = getTuner().getController().getFirmwareVersion(); + } } catch(Exception e) { @@ -428,66 +388,20 @@ public class HackRFTunerEditor extends TunerConfigurationEditor return sb.toString(); } - @Override - public void setItem(TunerConfiguration tunerConfiguration) - { - super.setItem(tunerConfiguration); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if(hasItem()) - { - HackRFTunerConfiguration config = getConfiguration(); - - if(tunerConfiguration.isAssigned()) - { - setControlsEnabled(true); - - mConfigurationName.setText(config.getName()); - mComboSampleRate.setSelectedItem(config.getSampleRate()); - mFrequencyCorrection.setValue(config.getFrequencyCorrection()); - mAmplifier.setSelected(config.getAmplifierEnabled()); - mComboLNAGain.setSelectedItem(config.getLNAGain()); - mComboVGAGain.setSelectedItem(config.getVGAGain()); - - //Update enabled state to reflect when frequency and sample rate controls are locked - mComboSampleRate.setEnabled(!mController.isLocked()); - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(config.getName()); - } - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(""); - } - - mLoading = false; - } - @Override public void save() { - if(hasItem() && !mLoading) + if(hasConfiguration() && !isLoading()) { - HackRFTunerConfiguration config = getConfiguration(); - - config.setName(mConfigurationName.getText()); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection(value); - config.setSampleRate((HackRFSampleRate)mComboSampleRate.getSelectedItem()); - config.setAmplifierEnabled(mAmplifier.isSelected()); - config.setLNAGain((HackRFLNAGain)mComboLNAGain.getSelectedItem()); - config.setVGAGain((HackRFVGAGain)mComboVGAGain.getSelectedItem()); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent(getConfiguration(), TunerConfigurationEvent.Event.CHANGE)); + getConfiguration().setFrequency(getFrequencyControl().getFrequency()); + double value = ((SpinnerNumberModel) getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); + getConfiguration().setFrequencyCorrection(value); + getConfiguration().setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected()); + getConfiguration().setSampleRate((HackRFSampleRate)getSampleRateCombo().getSelectedItem()); + getConfiguration().setAmplifierEnabled(getAmplifierToggle().isSelected()); + getConfiguration().setLNAGain((HackRFLNAGain)getLnaGainCombo().getSelectedItem()); + getConfiguration().setVGAGain((HackRFVGAGain)getVgaGainCombo().getSelectedItem()); + saveConfiguration(); } } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/CenterFrequencyCalculator.java b/src/main/java/io/github/dsheirer/source/tuner/manager/CenterFrequencyCalculator.java index dbf18db7..56e31c9b 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/CenterFrequencyCalculator.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/CenterFrequencyCalculator.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.manager; import io.github.dsheirer.source.tuner.TunerController; @@ -133,7 +136,7 @@ public class CenterFrequencyCalculator public static boolean canTune(TunerChannel channel, TunerController tunerController, SortedSet channels) { //Make sure we're within the tunable frequency range of this tuner - if(tunerController.getMinFrequency() < channel.getMinFrequency() && tunerController.getMaxFrequency() > channel.getMaxFrequency()) + if(tunerController.getMinimumFrequency() < channel.getMinFrequency() && tunerController.getMaximumFrequency() > channel.getMaxFrequency()) { //If this is the first lock, then we're good if(channels.isEmpty()) diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java index cdef6b0d..7bddf8f8 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.manager; @@ -50,6 +47,11 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor */ public abstract int getTunerChannelCount(); + /** + * Tells this channel source manager to shutdown all tuner channel sources to prepare for tuner shutdown. + */ + public abstract void stopAllChannels(); + /** * Obtains a source for the tuner channel or returns null if the channel cannot be sourced by this tuner. * diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java new file mode 100644 index 00000000..2197cc9c --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java @@ -0,0 +1,276 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.manager; + +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; +import io.github.dsheirer.source.tuner.Tuner; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A discovered tuner that may be accessible for use. + */ +public abstract class DiscoveredTuner implements ITunerErrorListener +{ + private Logger mLog = LoggerFactory.getLogger(DiscoveredTuner.class); + private TunerStatus mTunerStatus = TunerStatus.ENABLED; + private boolean mEnabled = true; + private String mErrorMessage; + private List mListeners = new CopyOnWriteArrayList(); + protected Tuner mTuner; + protected TunerConfiguration mTunerConfiguration; + + /** + * Tuner Class + */ + public abstract TunerClass getTunerClass(); + + /** + * Current status of the discovered tuner + */ + public TunerStatus getTunerStatus() + { + return mTunerStatus; + } + + /** + * Sets the status of the discovered tuner and notifies registered listeners of the status change. + * @param tunerStatus to set + */ + protected void setTunerStatus(TunerStatus tunerStatus) + { + TunerStatus previous = mTunerStatus; + mTunerStatus = tunerStatus; + broadcast(this, previous, mTunerStatus); + } + + /** + * Indicates if this discovered tuner is enabled and usable. + */ + public boolean isEnabled() + { + return mEnabled; + } + + /** + * Sets the enabled state of this discovered tuner + */ + public void setEnabled(boolean enabled) + { + //If there was a change in state + if(mEnabled ^ enabled) + { + mEnabled = enabled; + + if(mEnabled) + { + setTunerStatus(TunerStatus.ENABLED); + start(); + } + else + { + stop(); + setTunerStatus(TunerStatus.DISABLED); + } + } + } + + /** + * Indicates if this discovered tuner is available and usable. Use this method to check if a discovered tuner is + * available prior to access the tuner directly via the getTuner() method. + */ + public boolean isAvailable() + { + return getTunerStatus().isAvailable(); + } + + /** + * An identifier for this discovered tuner where this identifier when combined with the discovered tuner type + * is a globally unique value. + * + * Note: this identifier will be used to persist the enabled/disabled state of each discoverable tuner. Therefore, + * this identifier must be consistent across application run cycles in order to correctly manage disabled tuners. + * @return globally unique string identifier for the discovered tuner. + */ + public abstract String getId(); + + /** + * Access a started and initialized. + * + * Use the isAvailable() method to check if this tuner is available prior to invoking this method to avoid a + * null tuner instance. + * + * @return started and initialized tuner + */ + public Tuner getTuner() + { + return mTuner; + } + + /** + * Indicates if this discovered tuner is started and has a fully constructed tuner instance + */ + public boolean hasTuner() + { + return getTuner() != null; + } + + /** + * Tuner configuration for this tuner + */ + public TunerConfiguration getTunerConfiguration() + { + return mTunerConfiguration; + } + + /** + * Sets the tuner configuration for this tuner. + */ + public void setTunerConfiguration(TunerConfiguration tunerConfiguration) + { + mTunerConfiguration = tunerConfiguration; + + if(hasTuner()) + { + try + { + getTuner().getTunerController().apply(mTunerConfiguration); + } + catch(SourceException se) + { + mLog.error("Error applying tuner configuration [" + mTunerConfiguration.getClass() + + "] to discovered tuner [" + getId() + "}"); + } + } + } + + /** + * Indicates if this discovered tuner has a tuner configuration + */ + public boolean hasTunerConfiguration() + { + return mTunerConfiguration != null; + } + + /** + * Adds a tuner status change listener to monitor this discovered tuner for changes in status. + */ + public void addTunerStatusListener(IDiscoveredTunerStatusListener listener) + { + mListeners.add(listener); + } + + /** + * Removes a tuner status change listener from monitoring this discovered tuner for changes in status. + */ + public void removeTunerStatusListener(IDiscoveredTunerStatusListener listener) + { + mListeners.remove(listener); + } + + /** + * Broadcasts the tuner status change to all registered listeners. + * @param tuner that was changed + * @param previous tuner status + * @param current tuner status + */ + private void broadcast(DiscoveredTuner tuner, TunerStatus previous, TunerStatus current) + { + for(IDiscoveredTunerStatusListener listener: mListeners) + { + listener.tunerStatusUpdated(tuner, previous, current); + } + } + + /** + * Sets this tuner to an error state and applies the error message + * @param errorMessage to set + */ + @Override + public void setErrorMessage(String errorMessage) + { + mErrorMessage = errorMessage; + setTunerStatus(TunerStatus.ERROR); + stop(); + } + + /** + * Indicates if this tuner has an error message. + */ + public boolean hasErrorMessage() + { + return mErrorMessage != null; + } + + /** + * Optional error message. + * @return error message if there is one, or null if there is not. + */ + public String getErrorMessage() + { + return mErrorMessage; + } + + /** + * Fully instantiate and start this discovered tuner to make it usable within the application. Implementations + * should attempt to instantiate the tuner and assign it to mTuner variable. If there is an error, invoke the + * setErrorMessage() to signal the tuner is unusable. + */ + public abstract void start(); + + /** + * Attempts to restart a tuner that's currently in an error state + */ + public void reset() + { + if(getTunerStatus() == TunerStatus.ERROR) + { + mErrorMessage = null; + + if(isEnabled()) + { + setTunerStatus(TunerStatus.ENABLED); + } + else + { + setTunerStatus(TunerStatus.DISABLED); + } + } + } + + /** + * Stop this discovered tuner, notify registered listeners/consumers and release any resources that it is using. + */ + public void stop() + { + if(hasTuner()) + { + mLog.info("Stopping Tuner: " + getId()); + getTuner().stop(); + mTuner = null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredUSBTuner.java b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredUSBTuner.java new file mode 100644 index 00000000..2a1d1f99 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredUSBTuner.java @@ -0,0 +1,135 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.manager; + +import io.github.dsheirer.preference.source.ChannelizerType; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.TunerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A USB tuner that is discovered during the USB device traversal process that has a VID and PID that match a known + * USB tuner type that is currently supported by the software. + */ +public class DiscoveredUSBTuner extends DiscoveredTuner +{ + private Logger mLog = LoggerFactory.getLogger(DiscoveredUSBTuner.class); + private TunerClass mTunerClass; + private int mBus; + private int mPort; + private ChannelizerType mChannelizerType; + + /** + * Constructs an instance + * @param bus (USB) number + * @param port (USB) number + * @param channelizerType to use with the tuner + */ + public DiscoveredUSBTuner(TunerClass tunerClass, int bus, int port, ChannelizerType channelizerType) + { + mTunerClass = tunerClass; + mBus = bus; + mPort = port; + mChannelizerType = channelizerType; + } + + /** + * Indicates if this USB tuner is plugged into the specified bus and port. + * @param bus number + * @param port number + * @return true if there is a match + */ + public boolean isAt(int bus, int port) + { + return mBus == bus && mPort == port; + } + + /** + * Tuner class as determined from the USB VID and PID values + */ + public TunerClass getTunerClass() + { + return mTunerClass; + } + + /** + * USB bus number + */ + public int getBus() + { + return mBus; + } + + /** + * USB port number + */ + public int getPort() + { + return mPort; + } + + @Override + public String getId() + { + StringBuilder sb = new StringBuilder(); + sb.append(getTunerClass()); + sb.append(" USB Bus:").append(getBus()); + sb.append(" Port:").append(getPort()); + return sb.toString(); + } + + @Override + public void start() + { + if(isAvailable() && !hasTuner()) + { + try + { + mTuner = TunerFactory.getUsbTuner(getTunerClass(), getPort(), getBus(), this, mChannelizerType); + mTuner.start(); + } + catch(SourceException se) + { + + //Set error message to flag this tuner with error status and invoke stop() + setErrorMessage(se.getMessage()); + + mLog.error("Unable to start tuner [" + getTunerClass() + "] - error: " + getErrorMessage()); + + try + { + stop(); + } + catch(Exception e) + { + //No-op on attempted cleanup + } + } + } + } + + @Override + public String toString() + { + return "USB Tuner - " + getId(); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/FrequencyErrorCorrectionManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java similarity index 84% rename from src/main/java/io/github/dsheirer/source/tuner/FrequencyErrorCorrectionManager.java rename to src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java index ec93369d..7b5d68b1 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/FrequencyErrorCorrectionManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java @@ -1,21 +1,25 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.source.tuner; + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.manager; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.TunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +58,14 @@ public class FrequencyErrorCorrectionManager mEnabled = enabled; } + /** + * Indicates if Auto-PPM is enabled + */ + public boolean isEnabled() + { + return mEnabled; + } + /** * Resets monitoring */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java index 070c58fb..fa9fcc9d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java @@ -31,6 +31,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; @@ -45,12 +46,12 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager private final static int DELAY_BUFFER_DURATION_MILLISECONDS = 2000; -// private List mChannelSources = new CopyOnWriteArrayList<>(); private List mChannelSources = new CopyOnWriteArrayList<>(); private SortedSet mTunerChannels = new TreeSet<>(); private TunerController mTunerController; private ChannelSourceEventProcessor mChannelSourceEventProcessor = new ChannelSourceEventProcessor(); private NativeSampleDelayBuffer mSampleDelayBuffer; + private boolean mRunning = true; public HeterodyneChannelSourceManager(TunerController tunerController) { @@ -58,6 +59,26 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager mTunerController.addListener(this); } + @Override + public void stopAllChannels() + { + mRunning = false; + + List toStop = new ArrayList<>(mChannelSources); + + for(TunerChannelSource tunerChannelSource: toStop) + { + try + { + tunerChannelSource.process(SourceEvent.tunerShutdown(tunerChannelSource)); + } + catch(SourceException se) + { + mLog.error("Error stopping tuner channel source for tuner shutdown"); + } + } + } + @Override public SortedSet getTunerChannels() { @@ -73,14 +94,16 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager @Override public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification) { + if(!mRunning) + { + return null; + } + if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels)) { try { //Attempt to create the channel source first, in case we get a filter design exception -// CICTunerChannelSource tunerChannelSource = new CICTunerChannelSource(mChannelSourceEventProcessor, -// tunerChannel, mTunerController.getSampleRate(), channelSpecification); - HalfBandTunerChannelSource tunerChannelSource = new HalfBandTunerChannelSource(mChannelSourceEventProcessor, tunerChannel, mTunerController.getSampleRate(), channelSpecification); diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/IDiscoveredTunerStatusListener.java b/src/main/java/io/github/dsheirer/source/tuner/manager/IDiscoveredTunerStatusListener.java new file mode 100644 index 00000000..fd7ed6ce --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/IDiscoveredTunerStatusListener.java @@ -0,0 +1,34 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.manager; + +/** + * Discovered tuner status change listener interface + */ +public interface IDiscoveredTunerStatusListener +{ + /** + * Notification that a discovered tuner's status has changed. + * @param discoveredTuner that has a status change. + * @param previous tuner status + * @param current tuner status + */ + void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current); +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java index b01496a8..1584df83 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.manager; @@ -33,6 +30,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; @@ -48,12 +46,33 @@ public class PassThroughSourceManager extends ChannelSourceManager private TunerController mTunerController; private SortedSet mTunerChannels = new TreeSet<>(); private List mTunerChannelSources = new CopyOnWriteArrayList<>(); + private boolean mRunning = true; public PassThroughSourceManager(TunerController tunerController) { mTunerController = tunerController; } + @Override + public void stopAllChannels() + { + mRunning = false; + + List toStop = new ArrayList<>(mTunerChannelSources); + + for(TunerChannelSource tunerChannelSource: toStop) + { + try + { + tunerChannelSource.process(SourceEvent.tunerShutdown(tunerChannelSource)); + } + catch(SourceException se) + { + mLog.error("Error stopping tuner channel source for tuner shutdown"); + } + } + } + @Override public SortedSet getTunerChannels() { @@ -69,6 +88,11 @@ public class PassThroughSourceManager extends ChannelSourceManager @Override public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification) { + if(!mRunning) + { + return null; + } + PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(), mTunerController, tunerChannel); diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java index d769a02e..ac85201c 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java @@ -31,6 +31,11 @@ import org.slf4j.LoggerFactory; import java.util.SortedSet; +/** + * PolyphaseChannelSourceManager is responsible for managing the tuner's center tuned frequency and providing access to + * polyphase tuner channel sources (ie DDCs). This class is responsible for determining IF a requested channel can + * be provided and then adjusting the center frequency and provisioning a DDC Polyphase Tuner channel source. + */ public class PolyphaseChannelSourceManager extends ChannelSourceManager { private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSourceManager.class); @@ -38,10 +43,7 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager private TunerController mTunerController; /** - * PolyphaseChannelSourceManager is responsible for managing the tuner's center tuned frequency and providing access to - * polyphase tuner channel sources (ie DDCs). This class is responsible for determining IF a requested channel can - * be provided and then adjusting the center frequency and provisioning a DDC Polyphase Tuner channel source. - * + * Constructs an instance * @param tunerController with a center tuned frequency that will be managed by this instance */ public PolyphaseChannelSourceManager(TunerController tunerController) @@ -54,6 +56,12 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager mTunerController.addListener(mPolyphaseChannelManager); } + @Override + public void stopAllChannels() + { + mPolyphaseChannelManager.stopAllChannels(); + } + /** * Indicates if the channel min/max frequencies are within the tunable frequency range of the tuner controller * diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java new file mode 100644 index 00000000..5b0b0c4d --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java @@ -0,0 +1,560 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.manager; + +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.source.ChannelizerType; +import io.github.dsheirer.source.Source; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.config.SourceConfigTuner; +import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; +import io.github.dsheirer.source.config.SourceConfiguration; +import io.github.dsheirer.source.mixer.MixerManager; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.TunerType; +import io.github.dsheirer.source.tuner.channel.ChannelSpecification; +import io.github.dsheirer.source.tuner.channel.MultiFrequencyTunerChannelSource; +import io.github.dsheirer.source.tuner.channel.TunerChannel; +import io.github.dsheirer.source.tuner.channel.TunerChannelSource; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import io.github.dsheirer.source.tuner.configuration.TunerConfigurationManager; +import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; +import io.github.dsheirer.util.ThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.Context; +import org.usb4java.Device; +import org.usb4java.DeviceDescriptor; +import org.usb4java.DeviceList; +import org.usb4java.HotplugCallback; +import org.usb4java.HotplugCallbackHandle; +import org.usb4java.LibUsb; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * Tuner manager provides access to tuners using USB, recording, sound-card and system-daemon accessible devices. This + * manager also supports hot-plug detection and black-listing of discovered tuners so that they can be used with other + * software applications. + */ +public class TunerManager implements IDiscoveredTunerStatusListener +{ + private static final Logger mLog = LoggerFactory.getLogger(TunerManager.class); + private static final int MAXIMUM_USB_2_DATA_RATE = 480000000; + + private UserPreferences mUserPreferences; + private DiscoveredTunerModel mDiscoveredTunerModel = new DiscoveredTunerModel(); + private TunerConfigurationManager mTunerConfigurationManager; + private HotplugEventSupport mHotplugEventSupport = new HotplugEventSupport(); + private Context mLibUsbApplicationContext = new Context(); + private boolean mLibUsbInitialized = false; + + /** + * Constructs an instance + * @param userPreferences for preferences + */ + public TunerManager(UserPreferences userPreferences) + { + mUserPreferences = userPreferences; + mTunerConfigurationManager = new TunerConfigurationManager(userPreferences); + } + + /** + * Discovered tuner model + */ + public DiscoveredTunerModel getDiscoveredTunerModel() + { + return mDiscoveredTunerModel; + } + + /** + * Starts the tuner manager and loads all available tuners + */ + public void start() + { + mLog.info("LibUSB API Version: " + LibUsb.getApiVersion()); + mLog.info("LibUSB Version: " + LibUsb.getVersion()); + + int status = LibUsb.init(mLibUsbApplicationContext); + + if(status == LibUsb.SUCCESS) + { + mLibUsbInitialized = true; +// LibUsb.setOption(mLibUsbApplicationContext, LibUsb.OPTION_LOG_LEVEL, LibUsb.LOG_LEVEL_DEBUG); + mHotplugEventSupport.start(); + + discoverUSBTuners(); + } + + discoverSdrPlayTuners(); + discoverSoundCardTuners(); + discoverRecordingTuners(); + } + + /** + * Stops or shuts down the tuner manager and releases all devices + */ + public void stop() + { + //Stop all tuners + mDiscoveredTunerModel.releaseDiscoveredTuners(); + + //Shutdown LibUsb + if(mLibUsbInitialized) + { + mHotplugEventSupport.stop(); + LibUsb.exit(mLibUsbApplicationContext); + mLibUsbInitialized = false; + } + } + + /** + * Requests to save the current state of the tuner configurations + */ + public void saveConfigurations() + { + mTunerConfigurationManager.saveConfigurations(); + } + + /** + * Discover or rediscovers USB tuners + */ + private void discoverUSBTuners() + { + if(mLibUsbInitialized) + { + try + { + DeviceList deviceList = new DeviceList(); + int deviceCount = LibUsb.getDeviceList(mLibUsbApplicationContext, deviceList); + + if(deviceCount >= 0) + { + for(Device device: deviceList) + { + addUsbTuner(device); + } + } + + LibUsb.freeDeviceList(deviceList, true); + } + catch(Exception e) + { + mLog.error("LibUsb - error during USB device discovery", e); + } + } + } + + /** + * Determines if the USB device is a supported tuner and add if it has not already been added/discovered. + * @param device to inspect and optionally add as a discovered tuner + */ + private void addUsbTuner(Device device) + { + int bus = LibUsb.getBusNumber(device); + int port = LibUsb.getPortNumber(device); + + DeviceDescriptor deviceDescriptor = new DeviceDescriptor(); + int status = LibUsb.getDeviceDescriptor(device, deviceDescriptor); + + if(status == LibUsb.SUCCESS) + { + TunerClass tunerClass = TunerClass.lookup(deviceDescriptor.idVendor(), deviceDescriptor.idProduct()); + + if(tunerClass.isSupportedUsbTuner() && !mDiscoveredTunerModel.hasUsbTuner(bus, port)) + { + mLog.info("Adding USB Bus [" + bus + "] Port [" + port + "] ID [" + + String.format("%04X", deviceDescriptor.idVendor()) + ":" + + String.format("%04X", deviceDescriptor.idProduct()) + "] Tuner Class [" + tunerClass + "]"); + ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType(); + DiscoveredUSBTuner discoveredUSBTuner = new DiscoveredUSBTuner(tunerClass, bus, port, channelizerType); + discoveredUSBTuner.addTunerStatusListener(this); + + //Set the tuner to disabled if the user has previously blacklisted the tuner + if(mTunerConfigurationManager.isDisabled(discoveredUSBTuner)) + { + discoveredUSBTuner.setEnabled(false); + } + else + { + //Attempt to start the discovered tuner so we can determine the tuner type + discoveredUSBTuner.start(); + + if(discoveredUSBTuner.hasTuner()) + { + TunerType tunerType = discoveredUSBTuner.getTuner().getTunerType(); + + TunerConfiguration tunerConfiguration = mTunerConfigurationManager + .getTunerConfiguration(tunerType, discoveredUSBTuner.getId()); + + if(tunerConfiguration != null) + { + mLog.info("Applying tuner configuration to " + discoveredUSBTuner.getId()); + discoveredUSBTuner.setTunerConfiguration(tunerConfiguration); + mTunerConfigurationManager.saveConfigurations(); + } + } + } + + mDiscoveredTunerModel.addDiscoveredTuner(discoveredUSBTuner); + mLog.info("Tuner Added: " + discoveredUSBTuner); + } + } + else + { + mLog.error("LibUsb - unable to get device descriptor for device on bus [" + bus + + "] port [" + port + "] - status [" + status + "] - " + LibUsb.errorName(status)); + } + } + + /** + * Stops and removes the USB tuner if it is currently discovered. + * @param bus usb for the tuner + * @param port usb for the tuner + */ + private void removeUsbTuner(int bus, int port) + { + mDiscoveredTunerModel.removeUsbTuner(bus, port); + } + + /** + * Discover sound card tuners like the Funcube Dongle (Pro) + */ + private void discoverSoundCardTuners() + { + + } + + /** + * Discover SDRPlay RSP tuners + */ + private void discoverSdrPlayTuners() + { + //placeholder ... + } + + /** + * Discover recording based tuners + */ + private void discoverRecordingTuners() + { + //placeholder + } + + /** + * Handles tuner status change events. Events are sent to the tuner configuration manager so that it can save + * configuration updates and events are also monitored to detect when a user changes the tuner state of a tuner + * so that we can auto-start the tuner and apply a tuner configuration. + * + * @param discoveredTuner that has a status change. + * @param previous tuner status + * @param current tuner status + */ + @Override + public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current) + { + mTunerConfigurationManager.tunerStatusUpdated(discoveredTuner, previous, current); + + if(previous != TunerStatus.ENABLED && current == TunerStatus.ENABLED) + { + discoveredTuner.start(); + + if(discoveredTuner.hasTuner()) + { + TunerType tunerType = discoveredTuner.getTuner().getTunerType(); + + TunerConfiguration tunerConfiguration = mTunerConfigurationManager + .getTunerConfiguration(tunerType, discoveredTuner.getId()); + + if(tunerConfiguration != null) + { + discoveredTuner.setTunerConfiguration(tunerConfiguration); + mTunerConfigurationManager.saveConfigurations(); + } + } + } + } + + /** + * Find a tuner that matches the name argument + * + * @param preferredTunerName of the tuner + * @return named tuner or null + */ + public DiscoveredTuner getDiscoveredTuner(String preferredTunerName) + { + if(preferredTunerName != null) + { + for(DiscoveredTuner discoveredTuner : getDiscoveredTunerModel().getAvailableTuners()) + { + if(discoveredTuner.isAvailable() && + discoveredTuner.getTuner().getPreferredName().equalsIgnoreCase(preferredTunerName)) + { + return discoveredTuner; + } + } + } + + return null; + } + + /** + * Sorted list of preferred tuner names + */ + public List getPreferredTunerNames() + { + List preferredNames = new ArrayList<>(); + List availableTuners = getAvailableTuners(); + + for(DiscoveredTuner discoveredTuner: availableTuners) + { + String preferredName = discoveredTuner.getTuner().getPreferredName(); + + if(!preferredNames.contains(preferredName)) + { + preferredNames.add(preferredName); + } + } + + Collections.sort(preferredNames); + + return preferredNames; + } + + /** + * Tuners currently available for use + */ + public List getAvailableTuners() + { + return mDiscoveredTunerModel.getAvailableTuners(); + } + + public Source getSource(SourceConfiguration config, ChannelSpecification channelSpecification) throws SourceException + { + Source retVal = null; + + switch(config.getSourceType()) + { + case MIXER: + retVal = MixerManager.getSource(config); + break; + case TUNER: + if(config instanceof SourceConfigTuner) + { + SourceConfigTuner sourceConfigTuner = (SourceConfigTuner)config; + TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth()); + String preferredTuner = sourceConfigTuner.getPreferredTuner(); + retVal = getSource(tunerChannel, channelSpecification, preferredTuner); + } + break; + case TUNER_MULTIPLE_FREQUENCIES: + if(config instanceof SourceConfigTunerMultipleFrequency) + { + SourceConfigTunerMultipleFrequency sourceConfigTuner = (SourceConfigTunerMultipleFrequency)config; + TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth()); + String preferredTuner = sourceConfigTuner.getPreferredTuner(); + + Source source = getSource(tunerChannel, channelSpecification, preferredTuner); + + if(source instanceof TunerChannelSource) + { + retVal = new MultiFrequencyTunerChannelSource(this, (TunerChannelSource)source, + sourceConfigTuner.getFrequencies(), channelSpecification, sourceConfigTuner.getPreferredTuner()); + } + } + break; + default: + break; + } + + return retVal; + } + + /** + * Iterates current available tuners to get a tuner channel source for the specified frequency and bandwidth + * + * Returns null if no tuner can source the channel + */ + public Source getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification, String preferredTuner) + { + TunerChannelSource source = null; + + if(tunerChannel != null && channelSpecification != null) + { + DiscoveredTuner discoveredTuner; + + if(preferredTuner != null) + { + discoveredTuner = getDiscoveredTuner(preferredTuner); + + if(discoveredTuner != null) + { + try + { + source = discoveredTuner.getTuner().getChannelSourceManager().getSource(tunerChannel, channelSpecification); + + if(source != null) + { + return source; + } + } + catch(Exception e) + { + //Fall through to logger below + } + } + + mLog.info("Unable to source channel [" + tunerChannel.getFrequency() + "] from preferred tuner [" + + preferredTuner + "] - searching for another tuner"); + } + + Iterator it = mDiscoveredTunerModel.getAvailableTuners().iterator(); + + while(it.hasNext() && source == null) + { + discoveredTuner = it.next(); + + if(discoveredTuner.hasTuner()) + { + try + { + source = discoveredTuner.getTuner().getChannelSourceManager().getSource(tunerChannel, channelSpecification); + } + catch(Exception e) + { + mLog.error("Error obtaining channel from tuner [" + discoveredTuner.getTuner().getPreferredName() + "]", e); + } + } + } + } + + return source; + } + + /** + * USB hotplug event listener to register, unregister and detect when USB devices are added or removed. + * + * Note: hotplug is not supported on all platforms. + */ + public class HotplugEventSupport implements HotplugCallback + { + private static final int HOTPLUG_CONTINUE_EVENT_SUPPORT = 0; + private HotplugCallbackHandle mHotplugCallbackHandle; + private ScheduledFuture mEventProcessorFuture; + + /** + * LibUsb hotplug event notification + * @param context used for hotplug event registration + * @param device that arrived or left + * @param event value, arrived event or left event + * @param userData not used + * @return + */ + @Override + public int processEvent(Context context, Device device, int event, Object userData) + { + int bus = LibUsb.getBusNumber(device); + int port = LibUsb.getPortNumber(device); + + switch(event) + { + case LibUsb.HOTPLUG_EVENT_DEVICE_ARRIVED: + addUsbTuner(device); + break; + case LibUsb.HOTPLUG_EVENT_DEVICE_LEFT: + removeUsbTuner(bus, port); + break; + } + + return HOTPLUG_CONTINUE_EVENT_SUPPORT; + } + + /** + * Registers for hotplug event notifications, if the capability is supported on this platform and starts an + * event processing timer to process hotplug events. + */ + public void start() + { + if(LibUsb.hasCapability(LibUsb.CAP_HAS_HOTPLUG)) + { + mHotplugCallbackHandle = new HotplugCallbackHandle(); + int events = LibUsb.HOTPLUG_EVENT_DEVICE_ARRIVED | LibUsb.HOTPLUG_EVENT_DEVICE_LEFT; + int flags = LibUsb.HOTPLUG_ENUMERATE; + int vendorId = LibUsb.HOTPLUG_MATCH_ANY; + int productId = LibUsb.HOTPLUG_MATCH_ANY; + int deviceClass = LibUsb.HOTPLUG_MATCH_ANY; + + int status = LibUsb.hotplugRegisterCallback(mLibUsbApplicationContext, events, flags, vendorId, + productId, deviceClass, this, "sdrtrunk hotplug support", mHotplugCallbackHandle); + + if(status == LibUsb.SUCCESS) + { + Runnable eventHandler = () -> LibUsb.handleEvents(mLibUsbApplicationContext); + mEventProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(eventHandler, + 0, 100, TimeUnit.MILLISECONDS); + } + else + { + mHotplugCallbackHandle = null; + mLog.info("LibUsb - unable to register device hotplug listener - " + LibUsb.errorName(status)); + } + } + else + { + mLog.info("LibUsb Hotplug event notification Is Not Supported on this platform."); + } + } + + /** + * Unregisters from hotplug event notifications and stops the event processing timer + */ + public void stop() + { + if(mHotplugCallbackHandle != null) + { + LibUsb.hotplugDeregisterCallback(mLibUsbApplicationContext, mHotplugCallbackHandle); + mHotplugCallbackHandle = null; + } + + if(mEventProcessorFuture != null) + { + mEventProcessorFuture.cancel(true); + mEventProcessorFuture = null; + } + } + } + + public static void main(String[] args) + { + mLog.info("Starting ..."); + UserPreferences userPreferences = new UserPreferences(); + TunerManager tunerManager = new TunerManager(userPreferences); + tunerManager.start(); + + while(true); + +// mLog.info("Finished!"); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeSampleBuffer.java b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerStatus.java similarity index 51% rename from src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeSampleBuffer.java rename to src/main/java/io/github/dsheirer/source/tuner/manager/TunerStatus.java index 567d61d4..d32cb001 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeSampleBuffer.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerStatus.java @@ -17,35 +17,50 @@ * **************************************************************************** */ -package io.github.dsheirer.source.tuner.usb.converter; - -import io.github.dsheirer.sample.complex.ComplexSamples; -import io.github.dsheirer.sample.complex.InterleavedComplexSamples; - -import java.util.List; +package io.github.dsheirer.source.tuner.manager; /** - * Base class for native samples byte buffer. This class holds a copy of the byte array of samples - * that was copied from a native byte buffer into the JVM heap space. - * - * Consumers of this sample buffer must make a copy of the contents in either interleaved or non-interleaved - * sample array buffers. + * Current status of a discovered tuner. */ -public abstract class NativeSampleBuffer +public enum TunerStatus { - private byte[] mSamples; + /** + * A tuner that is enabled and usable, but not currently in-use. + */ + ENABLED("Enabled"), - public NativeSampleBuffer(byte[] samples) + /** + * A tuner that has been disabled or black-listed for use + */ + DISABLED("Disabled"), + + /** + * A tuner that has an error state and is unusable + */ + ERROR("Error"); + + private String mLabel; + + /** + * Constructs an instance + * @param label + */ + TunerStatus(String label) { - mSamples = samples; + mLabel = label; } - protected byte[] getSamples() + @Override + public String toString() { - return mSamples; + return mLabel; } - public abstract List convert(); - - public abstract List convertInterleaved(); + /** + * Indicates if this tuner is available and can be started + */ + public boolean isAvailable() + { + return this == ENABLED; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java b/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java index f2da0707..5bf5e4f9 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java @@ -1,30 +1,27 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.recording; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.properties.SystemProperties; -import io.github.dsheirer.source.tuner.TunerModel; +import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -54,7 +51,7 @@ public class AddRecordingTunerDialog extends JFrame private static final String SELECT_A_FILE = "Please select a recording file"; private static final String LAST_FILE_BROWSE_LOCATION_KEY = "AddRecordingTunerDialog.lastBrowseLocation"; private UserPreferences mUserPreferences; - private TunerModel mTunerModel; + private DiscoveredTunerModel mDiscoveredTunerModel; private JButton mSelectFileButton; private File mSelectedRecording; private JLabel mRecordingFileLabel; @@ -64,21 +61,12 @@ public class AddRecordingTunerDialog extends JFrame private JButton mCancelButton; private static final Pattern TUNER_RECORDING_PATTERN = Pattern.compile("TUNER_(\\d*)_baseband_\\d{8}_\\d{6}\\.wav"); - public AddRecordingTunerDialog(UserPreferences userPreferences, TunerModel tunerModel) + public AddRecordingTunerDialog(UserPreferences userPreferences, DiscoveredTunerModel discoveredTunerModel) { Validate.notNull(userPreferences, "UserPreferences cannot be null"); - Validate.notNull(tunerModel, "TunerModel cannot be null"); + Validate.notNull(discoveredTunerModel, "TunerModel cannot be null"); - if(!tunerModel.canAddRecordingTuner()) - { - JOptionPane.showMessageDialog(AddRecordingTunerDialog.this, - "Cannot add a recording tuner. Only one recording tuner can be loaded at any time", - "Cannot Add Recording Tuner", - JOptionPane.ERROR_MESSAGE); - return; - } - - mTunerModel = tunerModel; + mDiscoveredTunerModel = discoveredTunerModel; mUserPreferences = userPreferences; setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); @@ -206,13 +194,13 @@ public class AddRecordingTunerDialog extends JFrame try { - RecordingTuner tuner = new RecordingTuner(mUserPreferences); - mTunerModel.addTuner(tuner); - RecordingTunerController controller = tuner.getTunerController(); - RecordingTunerConfiguration config = new RecordingTunerConfiguration(); - config.setFrequency(frequency); - config.setPath(mSelectedRecording.getAbsolutePath()); - controller.apply(config); +// RecordingTuner tuner = new RecordingTuner(mUserPreferences); +// mDiscoveredTunerModel.addDiscoveredTuner(tuner); +// RecordingTunerController controller = tuner.getTunerController(); +// RecordingTunerConfiguration config = new RecordingTunerConfiguration(); +// config.setFrequency(frequency); +// config.setPath(mSelectedRecording.getAbsolutePath()); +// controller.apply(config); } catch(Exception ex) { diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java index 55a2038a..48028df5 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java @@ -1,29 +1,27 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.recording; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.source.ChannelizerType; import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; @@ -43,9 +41,9 @@ public class RecordingTuner extends Tuner private final int mInstanceID = mInstanceCounter++; private UserPreferences mUserPreferences; - public RecordingTuner(UserPreferences userPreferences) + public RecordingTuner(UserPreferences userPreferences, ITunerErrorListener tunerErrorListener) { - super("Recording Tuner", new RecordingTunerController()); + super(new RecordingTunerController(tunerErrorListener), tunerErrorListener); mUserPreferences = userPreferences; @@ -72,6 +70,12 @@ public class RecordingTuner extends Tuner } } + @Override + public String getPreferredName() + { + return "Recording Tuner-" + mInstanceID; + } + /** * Returns the tuner controller cast as a test tuner controller. */ @@ -83,7 +87,7 @@ public class RecordingTuner extends Tuner @Override public String getUniqueID() { - return getName() + "-" + mInstanceID; + return getPreferredName(); } @Override @@ -95,7 +99,7 @@ public class RecordingTuner extends Tuner @Override public TunerType getTunerType() { - return TunerClass.RECORDING_TUNER.getTunerType(); + return TunerType.RECORDING; } @Override diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java index 0d211a1b..0075836d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java @@ -1,27 +1,25 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.recording; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; @@ -38,18 +36,18 @@ public class RecordingTunerConfiguration extends TunerConfiguration //Empty jackson constructor } - public RecordingTunerConfiguration(String uniqueId, String name) - { - super(uniqueId, name); - } - - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @JsonIgnore @Override public TunerType getTunerType() { return TunerType.RECORDING; } + public RecordingTunerConfiguration(String uniqueId) + { + super(uniqueId); + } + @JacksonXmlProperty(isAttribute = true, localName = "path") public String getPath() { diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfigurationEditor.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfigurationEditor.java deleted file mode 100644 index f1cc5e0c..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfigurationEditor.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.source.tuner.recording; - -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.JLabel; -import javax.swing.JTextField; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; - -public class RecordingTunerConfigurationEditor extends TunerConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger(RecordingTunerConfigurationEditor.class); - - private JTextField mConfigurationName; - private JLabel mRecordingPath; - private boolean mLoading; - - private RecordingTunerController mController; - - public RecordingTunerConfigurationEditor(TunerConfigurationModel tunerConfigurationModel, RecordingTuner tuner) - { - super(tunerConfigurationModel); - - mController = tuner.getTunerController(); - - init(); - } - - @Override - public void setTunerLockState(boolean locked) - { - setControlsEnabled(!locked); - } - - private RecordingTunerConfiguration getConfiguration() - { - if(hasItem()) - { - return (RecordingTunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][grow]")); - - add(new JLabel("Recording Tuner Configuration"), "span,align center"); - - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() - { - @Override - public void focusLost(FocusEvent e) - { - save(); - } - - @Override - public void focusGained(FocusEvent e) - { - } - }); - - add(new JLabel("Name:")); - add(mConfigurationName, "span, wrap"); - - add(new JLabel("File:")); - mRecordingPath = new JLabel(); - add(mRecordingPath); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled(boolean enabled) - { - if(mConfigurationName.isEnabled() != enabled) - { - mConfigurationName.setEnabled(enabled); - } - } - - @Override - public void setItem(TunerConfiguration tunerConfiguration) - { - super.setItem(tunerConfiguration); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if(hasItem()) - { - RecordingTunerConfiguration config = getConfiguration(); - - setControlsEnabled(tunerConfiguration.isAssigned()); - mConfigurationName.setText(config.getName()); - mRecordingPath.setText(config.getPath()); - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(""); - mRecordingPath.setText(""); - } - - mLoading = false; - } - - @Override - public void save() - { - if(hasItem() && !mLoading) - { - RecordingTunerConfiguration config = getConfiguration(); - - config.setName(mConfigurationName.getText()); - - String path = mRecordingPath.getText(); - - if(path != null && !path.isEmpty()) - { - config.setPath(path); - - mLog.debug("Saving and setting controller with path: " + path); - try - { - mController.apply(config); - } - catch(SourceException se) - { - mLog.error("Error while applying recording tuner configuration", se); - } - } - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent(getConfiguration(), TunerConfigurationEvent.Event.CHANGE)); - } - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java index 77a846ba..710d3f59 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java @@ -22,7 +22,9 @@ import io.github.dsheirer.buffer.INativeBuffer; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.TunerController; +import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.wave.ComplexWaveSource; import org.slf4j.Logger; @@ -44,10 +46,33 @@ public class RecordingTunerController extends TunerController /** * Tuner controller testing implementation. + * @param tunerErrorListener to receive errors from this controller */ - public RecordingTunerController() + public RecordingTunerController(ITunerErrorListener tunerErrorListener) { - super(1000000l, 3000000000l, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH_PERCENTAGE); + super(tunerErrorListener); + setMinimumFrequency(1000000l); + setMaximumFrequency(3000000000l); + setMiddleUnusableHalfBandwidth(DC_NOISE_BANDWIDTH); + setUsableBandwidthPercentage(USABLE_BANDWIDTH_PERCENTAGE); + } + + @Override + public void start() throws SourceException + { + + } + + @Override + public void stop() + { + + } + + @Override + public TunerType getTunerType() + { + return TunerType.RECORDING; } /** @@ -115,14 +140,6 @@ public class RecordingTunerController extends TunerController return 0; } - - - @Override - public void dispose() - { - //no-op - } - @Override public void addBufferListener(Listener listener) { @@ -154,10 +171,11 @@ public class RecordingTunerController extends TunerController @Override public void apply(TunerConfiguration config) throws SourceException { - if(config instanceof RecordingTunerConfiguration) - { - RecordingTunerConfiguration rtc = (RecordingTunerConfiguration)config; + //Invoke super for frequency, frequency correction and autoPPM + super.apply(config); + if(config instanceof RecordingTunerConfiguration rtc) + { mCenterFrequency = rtc.getFrequency(); try diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java new file mode 100644 index 00000000..393fb028 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java @@ -0,0 +1,126 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.recording; + +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerEditor; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.JLabel; + +/** + * Recording tuner configuration editor + */ +public class RecordingTunerEditor extends TunerEditor +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger(RecordingTunerEditor.class); + private JLabel mRecordingPath; + private boolean mLoading; + + private RecordingTunerController mController; + + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager for saving configurations + * @param discoveredTuner for this configuration + */ + public RecordingTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) + { + super(userPreferences, tunerManager, discoveredTuner); +// mController = discoveredTuner.getTunerController(); +// init(); + } + + @Override + public void setTunerLockState(boolean locked) + { + super.setTunerLockState(locked); + } + + @Override + protected void tunerStatusUpdated() + { + + } + + private void init() + { + setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill]", + "[][][grow]")); + add(new JLabel("Recording Tuner Configuration"), "span,align center"); + add(new JLabel("File:")); + mRecordingPath = new JLabel(); + add(mRecordingPath); + } + +// @Override +// public void setItem(TunerConfiguration tunerConfiguration) +// { +// super.setItem(tunerConfiguration); +// +// //Toggle loading so that we don't fire a change event and schedule a settings file save +// mLoading = true; +// +// if(hasItem()) +// { +// RecordingTunerConfiguration config = getConfiguration(); +// +// mRecordingPath.setText(config.getPath()); +// } +// else +// { +// mRecordingPath.setText(""); +// } +// +// mLoading = false; +// } + + @Override + public void save() + { + if(hasConfiguration() && !mLoading) + { + RecordingTunerConfiguration config = getConfiguration(); + String path = mRecordingPath.getText(); + + if(path != null && !path.isEmpty()) + { + config.setPath(path); + + try + { + mController.apply(config); + } + catch(SourceException se) + { + mLog.error("Error while applying recording tuner configuration", se); + } + } + + saveConfiguration(); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/EmbeddedTuner.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/EmbeddedTuner.java new file mode 100644 index 00000000..c8d37e2f --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/EmbeddedTuner.java @@ -0,0 +1,117 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.rtl; + +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.TunerType; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import org.usb4java.LibUsbException; + +import javax.usb.UsbException; + +/** + * Tuner embedded in an RTL-2832 USB tuner. + * + * Note: since the RTL2832TunerController instance is constructed without knowing the embedded tuner type, we construct + * an instance of the actual tuner once the type is discovered and we use an adapter to access the non-public USB + * controls and interfaces needed by the embedded tuner. This access is through the RtlTunerControllerAdapter. + */ +public abstract class EmbeddedTuner +{ + private RTL2832TunerController.ControllerAdapter mAdapter; + + /** + * Constructs an instance + * @param adapter exposing the interfaces of the parent RTL2832TunerController + */ + public EmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter) + { + mAdapter = adapter; + } + + /** + * Access the interfaces of the parent RTL tuner controller via the adapter + * @return + */ + protected RTL2832TunerController.ControllerAdapter getAdapter() + { + return mAdapter; + } + + /** + * Initialize this embedded tuner. The calling method should ensure that the I2C repeater is enabled before + * calling this method. + */ + protected abstract void initTuner() throws UsbException; + + /** + * Type of embedded tuner + */ + public abstract TunerType getTunerType(); + + /** + * Apply the tuner configuration settings to this embedded tuner + * @param tunerConfig containing settings + * @throws SourceException if there is an error in applying the settings + */ + public abstract void apply(TunerConfiguration tunerConfig) throws SourceException; + + /** + * Minimum frequency supported by this tuner + */ + public abstract long getMinimumFrequencySupported(); + + /** + * Maximum frequency supported by this tuner + */ + public abstract long getMaximumFrequencySupported(); + + /** + * Bandwidth of the DC spike that is unusable for this tuner + */ + //TODO: can we get rid of this now? + public abstract int getDcSpikeHalfBandwidth(); + + /** + * Percentage of the bandwidth that is usable for this tuner + */ + public abstract double getUsableBandwidthPercent(); + + /** + * Sets the center tuned frequency + * @param frequency in Hertz + * @throws SourceException if there is an error + */ + public abstract void setTunedFrequency(long frequency) throws SourceException; + + /** + * Sets the sample mode for the tuner + * @param mode to set + * @throws LibUsbException + */ + public abstract void setSamplingMode(RTL2832TunerController.SampleMode mode) throws LibUsbException; + + /** + * Sets the filters according to the specified sample rate + * @param sampleRate for filter setup + * @throws SourceException if there is an error + */ + public abstract void setSampleRateFilters(int sampleRate) throws SourceException; +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java index 11baee44..3b1f2ff0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java @@ -1,47 +1,49 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.rtl; -import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.source.ChannelizerType; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * RTL-2832 Tuner + */ public class RTL2832Tuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger(RTL2832Tuner.class); - private TunerClass mTunerClass; - - public RTL2832Tuner(TunerClass tunerClass, RTL2832TunerController controller, UserPreferences userPreferences) - throws SourceException + /** + * Constructs an instance + * @param controller tuner + * @param channelizerType for the channelizer + * @param tunerErrorListener to listen for errors from this tuner + * @throws SourceException if there is an issue + */ + public RTL2832Tuner(RTL2832TunerController controller, ITunerErrorListener tunerErrorListener, + ChannelizerType channelizerType) { - super(tunerClass.getVendorDeviceLabel() + "/" + controller.getTunerType().getLabel() + " " + - controller.getUniqueID(), controller, userPreferences); - - mTunerClass = tunerClass; + super(controller, tunerErrorListener, channelizerType); } public RTL2832TunerController getController() @@ -49,6 +51,12 @@ public class RTL2832Tuner extends Tuner return (RTL2832TunerController)getTunerController(); } + @Override + public String getPreferredName() + { + return getUniqueID(); + } + @Override public String getUniqueID() { @@ -58,13 +66,7 @@ public class RTL2832Tuner extends Tuner @Override public TunerClass getTunerClass() { - return mTunerClass; - } - - @Override - public TunerType getTunerType() - { - return getController().getTunerType(); + return TunerClass.RTL2832; } @Override diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerConfiguration.java new file mode 100644 index 00000000..d5025ff1 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerConfiguration.java @@ -0,0 +1,64 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.rtl; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration; + +/** + * RTL2832 tuner configuration + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = E4KTunerConfiguration.class, name = "e4KTunerConfiguration"), + @JsonSubTypes.Type(value = R820TTunerConfiguration.class, name = "r820TTunerConfiguration"), +}) +@JacksonXmlRootElement(localName = "tuner_configuration") +public abstract class RTL2832TunerConfiguration extends TunerConfiguration +{ + private RTL2832TunerController.SampleRate mSampleRate = RTL2832TunerController.SampleRate.RATE_2_400MHZ; + + /** + * Default constructor to support Jackson + */ + public RTL2832TunerConfiguration() + { + } + + public RTL2832TunerConfiguration(String uniqueID) + { + super(uniqueID); + } + + @JacksonXmlProperty(isAttribute = true, localName = "sample_rate") + public RTL2832TunerController.SampleRate getSampleRate() + { + return mSampleRate; + } + + public void setSampleRate(RTL2832TunerController.SampleRate sampleRate) + { + mSampleRate = sampleRate; + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java index b39d1d95..090d1091 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java @@ -19,18 +19,17 @@ package io.github.dsheirer.source.tuner.rtl; import io.github.dsheirer.buffer.ByteNativeBufferFactory; -import io.github.dsheirer.buffer.INativeBuffer; -import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.buffer.INativeBufferFactory; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.ITunerErrorListener; -import io.github.dsheirer.source.tuner.TunerManager; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.TunerFactory; import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.usb.USBTransferProcessor; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager; import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; @@ -44,95 +43,175 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.locks.ReentrantLock; -public abstract class RTL2832TunerController extends USBTunerController +/** + * RTL-2832 tuner controller implementation. + */ +public class RTL2832TunerController extends USBTunerController { - private final static Logger mLog = LoggerFactory.getLogger(RTL2832TunerController.class); - - public final static int TWO_TO_22_POWER = 4194304; - public final static int USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE = 131072; - public final static int USB_TRANSFER_BUFFER_SIZE_LOW_SAMPLE_RATE = 8192; - public final static byte USB_INTERFACE = (byte) 0x0; - public final static byte CONTROL_ENDPOINT_IN = (byte) (LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR); - public final static byte CONTROL_ENDPOINT_OUT = (byte) (LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR); - public final static long TIMEOUT_US = 1000000l; //uSeconds - public final static byte REQUEST_ZERO = (byte) 0; - public final static byte EEPROM_ADDRESS = (byte) 0xA0; - protected ReentrantLock mLock = new ReentrantLock(); - - public final static byte[] sFIR_COEFFICIENTS = - { - (byte) 0xCA, (byte) 0xDC, (byte) 0xD7, (byte) 0xD8, (byte) 0xE0, - (byte) 0xF2, (byte) 0x0E, (byte) 0x35, (byte) 0x06, (byte) 0x50, - (byte) 0x9C, (byte) 0x0D, (byte) 0x71, (byte) 0x11, (byte) 0x14, - (byte) 0x71, (byte) 0x74, (byte) 0x19, (byte) 0x41, (byte) 0xA5 - }; + private static final Logger mLog = LoggerFactory.getLogger(RTL2832TunerController.class); + public static final int TWO_TO_22_POWER = 4194304; + public static final int USB_TRANSFER_BUFFER_SIZE = 65_536; + public static final byte CONTROL_ENDPOINT_IN = (byte) (LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR); + public static final byte CONTROL_ENDPOINT_OUT = (byte) (LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR); + public static final long TIMEOUT_US = 1000000l; //uSeconds + public static final byte REQUEST_ZERO = (byte) 0; + public static final byte EEPROM_ADDRESS = (byte) 0xA0; + public static final byte[] FIR_FILTER_COEFFICIENTS = {(byte) 0xCA, (byte) 0xDC, (byte) 0xD7, (byte) 0xD8, + (byte) 0xE0, (byte) 0xF2, (byte) 0x0E, (byte) 0x35, (byte) 0x06, (byte) 0x50, (byte) 0x9C, (byte) 0x0D, + (byte) 0x71, (byte) 0x11, (byte) 0x14, (byte) 0x71, (byte) 0x74, (byte) 0x19, (byte) 0x41, (byte) 0xA5}; public static final SampleRate DEFAULT_SAMPLE_RATE = SampleRate.RATE_2_400MHZ; - - protected Device mDevice; - protected DeviceDescriptor mDeviceDescriptor; - protected DeviceHandle mDeviceHandle; - private SampleRate mSampleRate = DEFAULT_SAMPLE_RATE; - - protected ByteNativeBufferFactory mNativeBufferFactory = new ByteNativeBufferFactory(); - protected int mOscillatorFrequency = 28800000; //28.8 MHz - protected USBTransferProcessor mUSBTransferProcessor; - protected Descriptor mDescriptor; + private INativeBufferFactory mNativeBufferFactory = new ByteNativeBufferFactory(); + private int mOscillatorFrequency = 28800000; //28.8 MHz + private Descriptor mDescriptor; + private EmbeddedTuner mEmbeddedTuner; + private long mTunedFrequency = 0; + private ReentrantLock mLock = new ReentrantLock(); /** - * Abstract tuner controller device. Use the static getTunerClass() method + * Abstract tuner controller device. Use the static gettype() method * to determine the tuner type, and construct the corresponding child * tuner controller class for that tuner type. */ - public RTL2832TunerController(Device device, DeviceDescriptor deviceDescriptor, long minTunableFrequency, - long maxTunableFrequency, int centerUnusableBandwidth, - double usableBandwidthPercentage) throws SourceException + public RTL2832TunerController(int bus, int port, ITunerErrorListener tunerErrorListener) { - super(minTunableFrequency, maxTunableFrequency, centerUnusableBandwidth, usableBandwidthPercentage); - mDevice = device; - mDeviceDescriptor = deviceDescriptor; + super(bus, port, tunerErrorListener); + } + + /** + * Lock for controlling configuration access to this tuner controller and the embedded tuner to ensure that + * configuration changes involving multiple USB control messages can happen atomically. There are at least two + * threads that can touch this controller: + * + * A: channel processing that changes the center tuned frequency and frequency correction. + * b: user ui thread that can touch any of the frequency correction and gain controls. + * + * Generally, we'll lock on each of the primary configuration change points - getter and setter for: + * Frequency + * Frequency Correction + * Sample Rate + * Gain Controls + * + * @return lock for controlling access to tuner controller and em + */ + private ReentrantLock getLock() + { + return mLock; + } + + + @Override + protected INativeBufferFactory getNativeBufferFactory() + { + return mNativeBufferFactory; + } + + @Override + protected int getTransferBufferSize() + { + return USB_TRANSFER_BUFFER_SIZE; } @Override public int getBufferSampleCount() { - return getUSBTransferBufferSize(getSampleRate()) / 2; //2 bytes per complex sample + return getTransferBufferSize() / 2; } - - public void init() throws SourceException + /** + * The embedded tuner used with this RTL2832 USB tuner + */ + public EmbeddedTuner getEmbeddedTuner() { - mDeviceHandle = new DeviceHandle(); + return mEmbeddedTuner; + } - int result = LibUsb.open(mDevice, mDeviceHandle); + public boolean hasEmbeddedTuner() + { + return mEmbeddedTuner != null; + } - if(result != LibUsb.SUCCESS) + /** + * Sets the tuned frequency in the embedded tuner + * @param frequency in Hertz + * @throws SourceException if there is an error + */ + @Override + public void setTunedFrequency(long frequency) throws SourceException + { + getEmbeddedTuner().setTunedFrequency(frequency); + mTunedFrequency = frequency; + } + + @Override + public long getTunedFrequency() throws SourceException + { + return mTunedFrequency; + } + + @Override + public void apply(TunerConfiguration tunerConfig) throws SourceException + { + //Invoke super for frequency, frequency correction and autoPPM + super.apply(tunerConfig); + + if(tunerConfig instanceof RTL2832TunerConfiguration rtl) { - mDeviceHandle = null; - - throw new SourceException("libusb couldn't open RTL2832 usb " - + "device [" + LibUsb.errorName(result) + "]"); + setSampleRate(rtl.getSampleRate()); + getEmbeddedTuner().apply(rtl); } + } - claimInterface(mDeviceHandle); + /** + * Executes additional device startup actions after being commanded by the parent USB startup actions. + * @throws SourceException if there is an error/issue making this device is unusable + */ + @Override + protected void deviceStart() throws SourceException + { + //Perform dummy write to see if device needs reset + boolean resetRequired = false; try { - setSampleRate(DEFAULT_SAMPLE_RATE); + writeRegister(Block.USB, Address.USB_SYSCTL.getAddress(), 0x09, 1); } - catch(Exception e) + catch(LibUsbException lue) { - throw new SourceException("RTL2832 Tuner Controller - couldn't " - + "set default sample rate", e); + if(lue.getErrorCode() < 0) + { + resetRequired = true; + } + else + { + mLog.error("Error performing test write to RTL2832 device - " + LibUsb.errorName(lue.getErrorCode())); + } } + if(resetRequired) + { + int status = LibUsb.resetDevice(getDeviceHandle()); + mLog.info("Resetting device - status: " + LibUsb.errorName(status)); + + try + { + writeRegister(Block.USB, Address.USB_SYSCTL.getAddress(), 0x09, 1); + } + catch(LibUsbException lue) + { + mLog.error("Couldnt' reset RTL2832 device - setting error"); + throw new SourceException("unable to reset USB device - " + LibUsb.errorName(status)); + } + } + + byte[] eeprom = null; try { /* Read the contents of the 256-byte EEPROM */ - eeprom = readEEPROM(mDeviceHandle, (short) 0, 256); + eeprom = readEEPROM((short) 0, 256); } catch(Exception e) { @@ -145,86 +224,71 @@ public abstract class RTL2832TunerController extends USBTunerController if(eeprom == null) { - mLog.error("eeprom byte array was null - constructed " - + "empty descriptor object"); + mLog.error("eeprom byte array was null - constructed empty descriptor object"); } } catch(Exception e) { - mLog.error("error while constructing device descriptor using " - + "descriptor byte array " + + mLog.error("error while constructing device descriptor using descriptor byte array " + (eeprom == null ? "[null]" : Arrays.toString(eeprom)), e); } - String deviceName = getTunerType().getLabel() + " " + getUniqueID(); + initBaseband(); - mUSBTransferProcessor = new RTL2832USBTransferProcessor(deviceName, mDeviceHandle, mNativeBufferFactory, - USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE, this); + TunerType tunerType = identifyTunerType(); + + if(tunerType == TunerType.UNKNOWN) + { + throw new SourceException("Unrecognized embedded tuner type for RTL-2832"); + } + + mEmbeddedTuner = TunerFactory.getRtlEmbeddedTuner(tunerType, new ControllerAdapter(this)); + + try + { + enableI2CRepeater(true); + getEmbeddedTuner().initTuner(); + enableI2CRepeater(false); + } + catch(UsbException ue) + { + throw new SourceException("Unable to initialize " + getEmbeddedTuner().getTunerType(), ue); + } + + setMinimumFrequency(mEmbeddedTuner.getMinimumFrequencySupported()); + setMaximumFrequency(mEmbeddedTuner.getMaximumFrequencySupported()); + setMiddleUnusableHalfBandwidth(mEmbeddedTuner.getDcSpikeHalfBandwidth()); + setUsableBandwidthPercentage(mEmbeddedTuner.getUsableBandwidthPercent()); + + try + { + setSampleRate(DEFAULT_SAMPLE_RATE); + } + catch(Exception e) + { + throw new SourceException("RTL2832 Tuner Controller - couldn't set default sample rate", e); + } } @Override - protected USBTransferProcessor getUSBTransferProcessor() + protected void deviceStop() { - return mUSBTransferProcessor; - } - - /** - * Claims the USB interface. Attempts to detach the active kernel driver - * if one is currently attached. - */ - public static void claimInterface(DeviceHandle handle) throws SourceException - { - if(handle != null) + try { - int result = LibUsb.kernelDriverActive(handle, USB_INTERFACE); - - if(result == 1) - { - result = LibUsb.detachKernelDriver(handle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + - LibUsb.errorName(result) + "]"); - - throw new SourceException("couldn't detach kernel driver " - + "from device"); - } - } - - result = LibUsb.claimInterface(handle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + - LibUsb.errorName(result) + "]"); - } + deinitBaseband(); } - else + catch(Exception e) { - throw new SourceException("couldn't claim usb interface - no " - + "device handle"); - } - } - - public static void releaseInterface(DeviceHandle handle) - throws SourceException - { - int result = LibUsb.releaseInterface(handle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't release interface [" + - LibUsb.errorName(result) + "]"); +// mLog.error("Error on device stop", e); + //No-op } } /** * Descriptor contains all identifiers and labels parsed from the EEPROM. * - * May return null if unable to get 256 byte eeprom descriptor from tuner - * or if the descriptor doesn't begin with byte values of 0x28 and 0x32 - * meaning it is a valid (and can be parsed) RTL2832 descriptor + * May return null if unable to get 256 byte eeprom descriptor from tuner or if the descriptor doesn't begin with + * byte values of 0x28 and 0x32 meaning it is a valid (and can be parsed) RTL2832 descriptor */ public Descriptor getDescriptor() { @@ -236,76 +300,45 @@ public abstract class RTL2832TunerController extends USBTunerController return null; } - public void setSamplingMode(SampleMode mode) throws LibUsbException + /** + * Manager for applying automatic frequency error PPM adjustments to the tuner controller based on + * frequency error measurements received from certain downstream decoders (e.g. P25). + * @return manager + */ + public FrequencyErrorCorrectionManager getFrequencyErrorCorrectionManager() { - switch(mode) - { - case QUADRATURE: - /* Set intermediate frequency to 0 Hz */ - setIFFrequency(0); - - /* Enable I/Q ADC Input */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x08, - (short) 0xCD, - 1); - - /* Enable zero-IF mode */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0xB1, - (short) 0x1B, - 1); - - /* Set default i/q path */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x06, - (short) 0x80, - 1); - break; - case DIRECT: - default: - throw new LibUsbException("QUADRATURE mode is the only mode " - + "currently supported", LibUsb.ERROR_NOT_SUPPORTED); - } + return mFrequencyErrorCorrectionManager; } - public void setIFFrequency(int frequency) throws LibUsbException - { - long ifFrequency = ((long) TWO_TO_22_POWER * (long) frequency) / - (long) mOscillatorFrequency * -1; - - /* Write byte 2 (high) */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x19, - (short) (Long.rotateRight(ifFrequency, 16) & 0x3F), - 1); - - /* Write byte 1 (middle) */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x1A, - (short) (Long.rotateRight(ifFrequency, 8) & 0xFF), - 1); - - /* Write byte 0 (low) */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x1B, - (short) (ifFrequency & 0xFF), - 1); - } - - public abstract void initTuner(boolean controlI2CRepeater) - throws UsbException; - /** - * Provides a unique identifier to use in distinctly identifying this - * tuner from among other tuners of the same type, so that we can fetch a - * tuner configuration from the settings manager for this specific tuner. + * Overrides updates for measured frequency error so that the updates can also be applied to the + * frequency error correction manager for automatic PPM updating. + * @param measuredFrequencyError in hertz averaged over a 5 second interval. + */ + @Override + public void setMeasuredFrequencyError(int measuredFrequencyError) + { + super.setMeasuredFrequencyError(measuredFrequencyError); + getFrequencyErrorCorrectionManager().updatePPM(getPPMFrequencyError()); + } + + private void setIFFrequency(int frequency) throws LibUsbException + { + long ifFrequency = ((long) TWO_TO_22_POWER * (long) frequency) / (long) mOscillatorFrequency * -1; + + /* Write byte 2 (high) */ + writeDemodRegister(Page.ONE, (short) 0x19, (short) (Long.rotateRight(ifFrequency, 16) & 0x3F), 1); + + /* Write byte 1 (middle) */ + writeDemodRegister(Page.ONE, (short) 0x1A, (short) (Long.rotateRight(ifFrequency, 8) & 0xFF), 1); + + /* Write byte 0 (low) */ + writeDemodRegister(Page.ONE, (short) 0x1B, (short) (ifFrequency & 0xFF), 1); + } + + /** + * Provides a unique identifier to use in distinctly identifying this tuner from among other tuners of the same + * type, so that we can fetch a tuner configuration from the settings manager for this specific tuner. * * @return serial number of the device */ @@ -313,129 +346,82 @@ public abstract class RTL2832TunerController extends USBTunerController { if(mDescriptor != null && mDescriptor.hasSerial()) { - return mDescriptor.getSerial(); + if(hasEmbeddedTuner()) + { + return TunerClass.RTL2832.toString() + "/" + getTunerType().getLabel() + " " + mDescriptor.getSerial(); + } + else + { + return TunerClass.RTL2832.toString() + " " + mDescriptor.getSerial(); + } } else { - int serial = - (0xFF & mDeviceDescriptor.iSerialNumber()); - - return "SER#" + serial; + int serial = (0xFF & getDeviceDescriptor().iSerialNumber()); + return TunerClass.RTL2832.toString() + " " + serial; } } - public abstract void setSampleRateFilters(int sampleRate) - throws SourceException; - - public abstract TunerType getTunerType(); - - public static TunerType identifyTunerType(Device device) - throws SourceException + /** + * Sets the filters for the sample rate + * @param sampleRate to setup + * @throws SourceException if there is an error + */ + public void setSampleRateFilters(int sampleRate) throws SourceException { - DeviceHandle handle = new DeviceHandle(); + getEmbeddedTuner().setSampleRateFilters(sampleRate); + } - int reason = LibUsb.open(device, handle); - - if(reason != LibUsb.SUCCESS) - { - throw new SourceException("couldn't open device - check permissions" - + " Linux: udev.rule? , or Windows: reinstall Zadig? [" + LibUsb.errorName(reason) + "]"); - } + /** + * Identifies the embedded tuner type + */ + public TunerType getTunerType() + { + return getEmbeddedTuner().getTunerType(); + } + /** + * Identifies the embedded tuner type + * @return tuner type + * @throws SourceException if there is an error + */ + private TunerType identifyTunerType() throws SourceException + { TunerType tunerClass = TunerType.UNKNOWN; try { - claimInterface(handle); - - /* Perform a dummy write to see if the device needs reset */ - boolean resetRequired = false; - - try - { - writeRegister(handle, - Block.USB, - Address.USB_SYSCTL.getAddress(), - 0x09, - 1); - } - catch(LibUsbException e) - { - - if(e.getErrorCode() < 0) - { - mLog.error("error performing dummy write - attempting " - + "device reset", e); - - resetRequired = true; - } - else - { - throw new SourceException("error performing dummy write " - + "to device [" + LibUsb.errorName( - e.getErrorCode()) + "]", e); - } - } - - if(resetRequired) - { - reason = LibUsb.resetDevice(handle); - - try - { - writeRegister(handle, - Block.USB, - Address.USB_SYSCTL.getAddress(), - 0x09, - 1); - } - catch(LibUsbException e2) - { - mLog.error("device reset attempted, but lost device handle. " - + "Try restarting the application to use this device"); - - throw new SourceException("couldn't reset device"); - } - } - - /* Initialize the baseband */ - initBaseband(handle); - - enableI2CRepeater(handle, true); + enableI2CRepeater(true); boolean controlI2CRepeater = false; /* Test for each tuner type until we find the correct one */ - if(isTuner(TunerTypeCheck.E4K, handle, controlI2CRepeater)) + if(isTuner(TunerTypeCheck.E4K, controlI2CRepeater)) { tunerClass = TunerType.ELONICS_E4000; } - else if(isTuner(TunerTypeCheck.FC0013, handle, controlI2CRepeater)) - { - tunerClass = TunerType.FITIPOWER_FC0013; - } - else if(isTuner(TunerTypeCheck.R820T, handle, controlI2CRepeater)) + else if(isTuner(TunerTypeCheck.R820T, controlI2CRepeater)) { tunerClass = TunerType.RAFAELMICRO_R820T; } - else if(isTuner(TunerTypeCheck.R828D, handle, controlI2CRepeater)) + else if(isTuner(TunerTypeCheck.FC0013, controlI2CRepeater)) + { + tunerClass = TunerType.FITIPOWER_FC0013; + } + else if(isTuner(TunerTypeCheck.R828D, controlI2CRepeater)) { tunerClass = TunerType.RAFAELMICRO_R828D; } - else if(isTuner(TunerTypeCheck.FC2580, handle, controlI2CRepeater)) + else if(isTuner(TunerTypeCheck.FC2580, controlI2CRepeater)) { tunerClass = TunerType.FCI_FC2580; } - else if(isTuner(TunerTypeCheck.FC0012, handle, controlI2CRepeater)) + else if(isTuner(TunerTypeCheck.FC0012, controlI2CRepeater)) { tunerClass = TunerType.FITIPOWER_FC0012; } - enableI2CRepeater(handle, false); - - releaseInterface(handle); - - LibUsb.close(handle); + enableI2CRepeater(false); } catch(Exception e) { @@ -445,121 +431,109 @@ public abstract class RTL2832TunerController extends USBTunerController return tunerClass; } - /** - * Releases the USB interface - */ - public void release() + @Override + protected void prepareStreaming() { - try - { - if(mUSBTransferProcessor != null) - { - mUSBTransferProcessor.removeListener(); - TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(mUSBTransferProcessor); - } - - LibUsb.releaseInterface(mDeviceHandle, USB_INTERFACE); - } - catch(Exception e) - { - mLog.error("attempt to release USB interface failed", e); - } + resetUSBBuffer(); } + /** + * Resets the USB buffer + * @throws LibUsbException if there is an error + */ public void resetUSBBuffer() throws LibUsbException { - writeRegister(mDeviceHandle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); - writeRegister(mDeviceHandle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x0000, 2); + writeRegister(Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); + writeRegister(Block.USB, Address.USB_EPA_CTL.getAddress(), 0x0000, 2); } - public static void initBaseband(DeviceHandle handle) throws LibUsbException + /** + * Initializes the RF baseband system + * @throws LibUsbException if there is an error + */ + public void initBaseband() throws LibUsbException { /* Initialize USB */ - writeRegister(handle, Block.USB, Address.USB_SYSCTL.getAddress(), 0x09, 1); - writeRegister(handle, Block.USB, Address.USB_EPA_MAXPKT.getAddress(), 0x0002, 2); - writeRegister(handle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); + writeRegister(Block.USB, Address.USB_SYSCTL.getAddress(), 0x09, 1); + writeRegister(Block.USB, Address.USB_EPA_MAXPKT.getAddress(), 0x0002, 2); + writeRegister(Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); /* Power on demod */ - writeRegister(handle, Block.SYS, Address.DEMOD_CTL_1.getAddress(), 0x22, 1); - writeRegister(handle, Block.SYS, Address.DEMOD_CTL.getAddress(), 0xE8, 1); + writeRegister(Block.SYS, Address.DEMOD_CTL_1.getAddress(), 0x22, 1); + writeRegister(Block.SYS, Address.DEMOD_CTL.getAddress(), 0xE8, 1); /* Reset demod */ - writeDemodRegister(handle, Page.ONE, (short) 0x01, 0x14, 1); //Bit 3 = soft reset - writeDemodRegister(handle, Page.ONE, (short) 0x01, 0x10, 1); + writeDemodRegister(Page.ONE, (short) 0x01, 0x14, 1); //Bit 3 = soft reset + writeDemodRegister(Page.ONE, (short) 0x01, 0x10, 1); /* Disable spectrum inversion and adjacent channel rejection */ - writeDemodRegister(handle, Page.ONE, (short) 0x15, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x16, 0x0000, 2); + writeDemodRegister(Page.ONE, (short) 0x15, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x16, 0x0000, 2); /* Clear DDC shift and IF frequency registers */ - writeDemodRegister(handle, Page.ONE, (short) 0x16, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x17, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x18, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x19, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x1A, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x1B, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x16, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x17, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x18, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x19, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x1A, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x1B, 0x00, 1); /* Set FIR coefficients */ - for(int x = 0; x < sFIR_COEFFICIENTS.length; x++) + for(int x = 0; x < FIR_FILTER_COEFFICIENTS.length; x++) { - writeDemodRegister(handle, - Page.ONE, - (short) (0x1C + x), - sFIR_COEFFICIENTS[x], - 1); + writeDemodRegister(Page.ONE, (short) (0x1C + x), FIR_FILTER_COEFFICIENTS[x], 1); } /* Enable SDR mode, disable DAGC (bit 5) */ - writeDemodRegister(handle, Page.ZERO, (short) 0x19, 0x05, 1); + writeDemodRegister(Page.ZERO, (short) 0x19, 0x05, 1); /* Init FSM state-holding register */ - writeDemodRegister(handle, Page.ONE, (short) 0x93, 0xF0, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x94, 0x0F, 1); + writeDemodRegister(Page.ONE, (short) 0x93, 0xF0, 1); + writeDemodRegister(Page.ONE, (short) 0x94, 0x0F, 1); /* Disable AGC (en_dagc, bit 0) (seems to have no effect) */ - writeDemodRegister(handle, Page.ONE, (short) 0x11, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x11, 0x00, 1); /* Disable RF and IF AGC loop */ - writeDemodRegister(handle, Page.ONE, (short) 0x04, 0x00, 1); + writeDemodRegister(Page.ONE, (short) 0x04, 0x00, 1); /* Disable PID filter */ - writeDemodRegister(handle, Page.ZERO, (short) 0x61, 0x60, 1); + writeDemodRegister(Page.ZERO, (short) 0x61, 0x60, 1); /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ - writeDemodRegister(handle, Page.ZERO, (short) 0x06, 0x80, 1); + writeDemodRegister(Page.ZERO, (short) 0x06, 0x80, 1); /* Enable Zero-if mode (en_bbin bit), * DC cancellation (en_dc_est), * IQ estimation/compensation (en_iq_comp, en_iq_est) */ - writeDemodRegister(handle, Page.ONE, (short) 0xB1, 0x1B, 1); + writeDemodRegister(Page.ONE, (short) 0xB1, 0x1B, 1); /* Disable 4.096 MHz clock output on pin TP_CK0 */ - writeDemodRegister(handle, Page.ZERO, (short) 0x0D, 0x83, 1); + writeDemodRegister(Page.ZERO, (short) 0x0D, 0x83, 1); } - protected void deinitBaseband(DeviceHandle handle) - throws IllegalArgumentException, UsbDisconnectedException, UsbException + /** + * Turns off the RF baseband system + * @throws IllegalArgumentException never + * @throws UsbDisconnectedException of the device is no longer accessible + */ + private void deinitBaseband() throws IllegalArgumentException, UsbDisconnectedException { - writeRegister(handle, Block.SYS, Address.DEMOD_CTL.getAddress(), 0x20, 1); + writeRegister(Block.SYS, Address.DEMOD_CTL.getAddress(), 0x20, 1); } /** * Sets the General Purpose Input/Output (GPIO) register bit * - * @param handle - USB tuner device - * @param bitMask - bit mask with one for targeted register bits and zero - * for the non-targeted register bits + * @param bitMask - bit mask with one for targeted register bits and zero for the non-targeted register bits * @param enabled - true to set the bit and false to clear the bit * @throws UsbDisconnectedException - if the tuner device is disconnected - * @throws UsbException - if there is a USB error while communicating with - * the device + * @throws UsbException - if there is a USB error while communicating with the device */ - protected static void setGPIOBit(DeviceHandle handle, - byte bitMask, - boolean enabled) throws LibUsbException + private void setGPIOBit(byte bitMask, boolean enabled) throws LibUsbException { //Get current register value - int value = readRegister(handle, Block.SYS, Address.GPO.getAddress(), 1); + int value = readRegister(Block.SYS, Address.GPO.getAddress(), 1); //Update the masked bits if(enabled) @@ -572,103 +546,99 @@ public abstract class RTL2832TunerController extends USBTunerController } //Write the change back to the device - writeRegister(handle, Block.SYS, Address.GPO.getAddress(), value, 1); + writeRegister(Block.SYS, Address.GPO.getAddress(), value, 1); } /** * Enables GPIO Output * - * @param handle - usb tuner device * @param bitMask - mask containing one bit value in targeted bit field(s) * @throws UsbDisconnectedException * @throws UsbException */ - protected static void setGPIOOutput(DeviceHandle handle, byte bitMask) - throws LibUsbException + private void setGPIOOutput(byte bitMask) throws LibUsbException { //Get current register value - int value = readRegister(handle, Block.SYS, Address.GPD.getAddress(), 1); + int value = readRegister(Block.SYS, Address.GPD.getAddress(), 1); //Mask the value and rewrite it - writeRegister(handle, Block.SYS, Address.GPO.getAddress(), - value & ~bitMask, 1); + writeRegister(Block.SYS, Address.GPO.getAddress(), value & ~bitMask, 1); //Get current register value - value = readRegister(handle, Block.SYS, Address.GPOE.getAddress(), 1); + value = readRegister(Block.SYS, Address.GPOE.getAddress(), 1); //Mask the value and rewrite it - writeRegister(handle, Block.SYS, Address.GPOE.getAddress(), - value | bitMask, 1); + writeRegister(Block.SYS, Address.GPOE.getAddress(), value | bitMask, 1); } - protected static void enableI2CRepeater(DeviceHandle handle, - boolean enabled) - throws LibUsbException + /** + * Enables or disables the I2C repeater + * @param enabled to turn I2C on or off + * @throws LibUsbException if there is an error + */ + private void enableI2CRepeater(boolean enabled) throws LibUsbException { Page page = Page.ONE; short address = 1; - int value; - - if(enabled) - { - value = 0x18; //ON - } - else - { - value = 0x10; //OFF - } - - writeDemodRegister(handle, page, address, value, 1); + int value = (enabled ? 0x18 : 0x10); + writeDemodRegister(page, address, value, 1); } - protected boolean isI2CRepeaterEnabled() throws SourceException + /** + * Indicates if the I2C repeater is currently enabled or turned on. + */ + private boolean isI2CRepeaterEnabled() { - int register = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x1, 1); - - return register == 0x18; + return readDemodRegister(Page.ONE, (short) 0x1, 1) == 0x18; } - protected static int readI2CRegister(DeviceHandle handle, - byte i2CAddress, - byte i2CRegister, - boolean controlI2CRepeater) - throws LibUsbException + /** + * Reads the I2C register + * @param i2CAddress address of the register + * @param i2CRegister register + * @param controlI2CRepeater set to true to have this method turn repeater on/off for this invocation or false if + * the repeater has already been enabled. + * @return register value + * @throws LibUsbException if there is an error + */ + private int readI2CRegister(byte i2CAddress, byte i2CRegister, boolean controlI2CRepeater) throws LibUsbException { short address = (short) (i2CAddress & 0xFF); - ByteBuffer buffer = ByteBuffer.allocateDirect(1); buffer.put(i2CRegister); buffer.rewind(); - ByteBuffer data = ByteBuffer.allocateDirect(1); if(controlI2CRepeater) { - enableI2CRepeater(handle, true); - - write(handle, address, Block.I2C, buffer); - read(handle, address, Block.I2C, data); - - enableI2CRepeater(handle, false); + enableI2CRepeater(true); + write(address, Block.I2C, buffer); + read(address, Block.I2C, data); + enableI2CRepeater(false); } else { - write(handle, address, Block.I2C, buffer); - read(handle, address, Block.I2C, data); + write(address, Block.I2C, buffer); + read(address, Block.I2C, data); } - return (int) (data.get() & 0xFF); + return (data.get() & 0xFF); } - protected void writeI2CRegister(DeviceHandle handle, - byte i2CAddress, - byte i2CRegister, - byte value, - boolean controlI2CRepeater) throws LibUsbException + /** + * Writes to the I2C register + * @param i2CAddress of the register + * @param i2CRegister register + * @param value to write + * @param controlI2CRepeater set to true to have this method turn repeater on/off for this invocation or false if + * the repeater has already been enabled. + * @throws LibUsbException if there is an error + */ + private void writeI2CRegister(byte i2CAddress, byte i2CRegister, byte value, boolean controlI2CRepeater) + throws LibUsbException { short address = (short) (i2CAddress & 0xFF); - ByteBuffer buffer = ByteBuffer.allocateDirect(2); buffer.put(i2CRegister); buffer.put(value); @@ -677,21 +647,25 @@ public abstract class RTL2832TunerController extends USBTunerController if(controlI2CRepeater) { - enableI2CRepeater(handle, true); - write(handle, address, Block.I2C, buffer); - enableI2CRepeater(handle, false); + enableI2CRepeater(true); + write(address, Block.I2C, buffer); + enableI2CRepeater(false); } else { - write(handle, address, Block.I2C, buffer); + write(address, Block.I2C, buffer); } } - protected static void writeDemodRegister(DeviceHandle handle, - Page page, - short address, - int value, - int length) throws LibUsbException + /** + * Writes to the demod register + * @param page of register + * @param address of resister + * @param value to write + * @param length of value in bytes + * @throws LibUsbException on error + */ + private void writeDemodRegister(Page page, short address, int value, int length) throws LibUsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(length); buffer.order(ByteOrder.BIG_ENDIAN); @@ -706,47 +680,53 @@ public abstract class RTL2832TunerController extends USBTunerController } else { - throw new IllegalArgumentException("Cannot write value greater " - + "than 16 bits to the register - length [" + length + "]"); + throw new IllegalArgumentException("Cannot write value greater than 16 bits to the register - length [" + + length + "]"); } short index = (short) (0x10 | page.getPage()); - short newAddress = (short) (address << 8 | 0x20); - - write(handle, newAddress, index, buffer); - - readDemodRegister(handle, Page.TEN, (short) 1, length); + write(newAddress, index, buffer); + readDemodRegister(Page.TEN, (short) 1, length); } - protected static int readDemodRegister(DeviceHandle handle, - Page page, - short address, - int length) throws LibUsbException + /** + * Reads the demod register + * @param page of register + * @param address of register + * @param length of value to read in bytes + * @return value read + * @throws LibUsbException on error + */ + private int readDemodRegister(Page page, short address, int length) throws LibUsbException { short index = page.getPage(); short newAddress = (short) ((address << 8) | 0x20); ByteBuffer buffer = ByteBuffer.allocateDirect(length); - read(handle, newAddress, index, buffer); + read(newAddress, index, buffer); buffer.order(ByteOrder.LITTLE_ENDIAN); if(length == 2) { - return (int) (buffer.getShort() & 0xFFFF); + return (buffer.getShort() & 0xFFFF); } else { - return (int) (buffer.get() & 0xFF); + return (buffer.get() & 0xFF); } } - protected static void writeRegister(DeviceHandle handle, - Block block, - short address, - int value, - int length) throws LibUsbException + /** + * Writes a value to the register + * @param block of register + * @param address of register + * @param value to write + * @param length of value in bytes + * @throws LibUsbException on error + */ + private void writeRegister(Block block, short address, int value, int length) throws LibUsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(length); buffer.order(ByteOrder.BIG_ENDIAN); @@ -761,97 +741,92 @@ public abstract class RTL2832TunerController extends USBTunerController } else { - throw new IllegalArgumentException("Cannot write value greater " - + "than 16 bits to the register - length [" + length + "]"); + throw new IllegalArgumentException("Cannot write value greater than 16 bits to the register - length [" + + length + "]"); } buffer.rewind(); - - write(handle, address, block, buffer); + write(address, block, buffer); } - protected static int readRegister(DeviceHandle handle, - Block block, - short address, - int length) throws LibUsbException + /** + * Reads a value from a register + * @param block of register + * @param address of register + * @param length of value to read + * @return value read + * @throws LibUsbException on error + */ + private int readRegister(Block block, short address, int length) throws LibUsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(2); - - read(handle, address, block, buffer); - + read(address, block, buffer); buffer.order(ByteOrder.LITTLE_ENDIAN); if(length == 2) { - return (int) (buffer.getShort() & 0xFFFF); + return (buffer.getShort() & 0xFFFF); } else { - return (int) (buffer.get() & 0xFF); + return (buffer.get() & 0xFF); } } /** + * Writes a value + * @param address of register + * @param block of register + * @param buffer containing value to write + * @throws LibUsbException on error */ - protected static void write(DeviceHandle handle, - short address, - Block block, - ByteBuffer buffer) throws LibUsbException + private void write(short address, Block block, ByteBuffer buffer) throws LibUsbException { - write(handle, address, block.getWriteIndex(), buffer); + write(address, block.getWriteIndex(), buffer); } - protected static void write(DeviceHandle handle, - short value, - short index, - ByteBuffer buffer) throws LibUsbException + /** + * Performs a control type write + * @param value to write + * @param index to write value to + * @param buffer contents to write + * @throws LibUsbException if there is an error + */ + private void write(short value, short index, ByteBuffer buffer) throws LibUsbException { - if(handle != null) + if(hasDeviceHandle()) { - int transferred = LibUsb.controlTransfer(handle, - CONTROL_ENDPOINT_OUT, - REQUEST_ZERO, - value, - index, - buffer, - TIMEOUT_US); + int transferred = LibUsb.controlTransfer(getDeviceHandle(), CONTROL_ENDPOINT_OUT, REQUEST_ZERO, value, + index, buffer, TIMEOUT_US); if(transferred < 0) { - throw new LibUsbException("error writing byte buffer", - transferred); + throw new LibUsbException("error writing byte buffer", transferred); } else if(transferred != buffer.capacity()) { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + + throw new LibUsbException("transferred bytes [" + transferred + "] is not what was expected [" + buffer.capacity() + "]", transferred); } } else { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); + throw new LibUsbException("device handle is null", LibUsb.ERROR_NO_DEVICE); } } /** * Performs a control type read + * @param address to read + * @param index to read + * @param buffer to store read content */ - protected static void read(DeviceHandle handle, - short address, - short index, - ByteBuffer buffer) throws LibUsbException + private void read(short address, short index, ByteBuffer buffer) throws LibUsbException { - if(handle != null) + if(isRunning()) { - int transferred = LibUsb.controlTransfer(handle, - CONTROL_ENDPOINT_IN, - REQUEST_ZERO, - address, - index, - buffer, - TIMEOUT_US); + int transferred = LibUsb.controlTransfer(getDeviceHandle(), CONTROL_ENDPOINT_IN, REQUEST_ZERO, address, + index, buffer, TIMEOUT_US); if(transferred < 0) { @@ -859,16 +834,10 @@ public abstract class RTL2832TunerController extends USBTunerController } else if(transferred != buffer.capacity()) { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); + throw new LibUsbException("transferred bytes [" + transferred + "] is not what was expected [" + + buffer.capacity() + "]", transferred); } } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } } /** @@ -876,42 +845,33 @@ public abstract class RTL2832TunerController extends USBTunerController * * @return big-endian byte array (needs to be swapped to be usable) */ - protected static void read(DeviceHandle handle, - short address, - Block block, - ByteBuffer buffer) throws LibUsbException + private void read(short address, Block block, ByteBuffer buffer) throws LibUsbException { - read(handle, address, block.getReadIndex(), buffer); + read(address, block.getReadIndex(), buffer); } /** * Tests if the specified tuner type is contained in the usb tuner device. * * @param type - tuner type to test for - * @param handle - handle to the usb tuner device - * @param controlI2CRepeater - indicates if the method should control the - * I2C repeater independently + * @param controlI2CRepeater - indicates if the method should control the I2C repeater independently * @return - true if the device is the specified tuner type */ - protected static boolean isTuner(TunerTypeCheck type, - DeviceHandle handle, - boolean controlI2CRepeater) + private boolean isTuner(TunerTypeCheck type, boolean controlI2CRepeater) { try { - if(type == TunerTypeCheck.FC0012 || - type == TunerTypeCheck.FC2580) + if(type == TunerTypeCheck.FC0012 || type == TunerTypeCheck.FC2580) { /* Initialize the GPIOs */ - setGPIOOutput(handle, (byte) 0x20); + setGPIOOutput((byte) 0x20); /* Reset tuner before probing */ - setGPIOBit(handle, (byte) 0x20, true); - setGPIOBit(handle, (byte) 0x20, false); + setGPIOBit((byte) 0x20, true); + setGPIOBit((byte) 0x20, false); } - int value = readI2CRegister(handle, type.getI2CAddress(), - type.getCheckAddress(), controlI2CRepeater); + int value = readI2CRegister(type.getI2CAddress(), type.getCheckAddress(), controlI2CRepeater); if(type == TunerTypeCheck.FC2580) { @@ -937,14 +897,16 @@ public abstract class RTL2832TunerController extends USBTunerController public int getSampleRateFromTuner() throws SourceException { + int returnRate = DEFAULT_SAMPLE_RATE.getRate(); + + getLock().lock(); + try { - int high = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x9F, 2); - int low = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0xA1, 2); - + int high = readDemodRegister(Page.ONE, (short) 0x9F, 2); + int low = readDemodRegister(Page.ONE, (short) 0xA1, 2); int ratio = Integer.rotateLeft(high, 16) | low; - - int rate = (int) (mOscillatorFrequency * TWO_TO_22_POWER / ratio); + int rate = (mOscillatorFrequency * TWO_TO_22_POWER / ratio); SampleRate sampleRate = SampleRate.getClosest(rate); @@ -952,47 +914,55 @@ public abstract class RTL2832TunerController extends USBTunerController if(sampleRate.getRate() != rate) { setSampleRate(sampleRate); - - return sampleRate.getRate(); + returnRate = sampleRate.getRate(); } } catch(Exception e) { - throw new SourceException("RTL2832 Tuner Controller - cannot get " - + "current sample rate", e); + throw new SourceException("RTL2832 Tuner Controller - cannot get current sample rate", e); + } + finally + { + getLock().unlock(); } - return DEFAULT_SAMPLE_RATE.getRate(); + return returnRate; } + /** + * Sets the sampling rate of the RTL2832 + * @param sampleRate to apply + * @throws SourceException + */ public void setSampleRate(SampleRate sampleRate) throws SourceException { - /* Write high-order 16-bits of sample rate ratio to demod register */ - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x9F, - sampleRate.getRatioHighBits(), 2); + getLock().lock(); - /* Write low-order 16-bits of sample rate ratio to demod register. - * Note: none of the defined rates have a low order ratio value, so we - * simply write a zero to the register */ - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0xA1, 0, 2); - - /* Set sample rate correction to 0 */ - setSampleRateFrequencyCorrection(0); - - /* Reset the demod for the changes to take effect */ - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x01, 0x14, 1); - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x01, 0x10, 1); - - /* Apply any tuner specific sample rate filter settings */ - setSampleRateFilters(sampleRate.getRate()); - - mSampleRate = sampleRate; - - mFrequencyController.setSampleRate(sampleRate.getRate()); - - if(mUSBTransferProcessor != null) + try { - mUSBTransferProcessor.setBufferSize(getUSBTransferBufferSize(sampleRate.getRate())); + /* Write high-order 16-bits of sample rate ratio to demod register */ + writeDemodRegister(Page.ONE, (short) 0x9F, sampleRate.getRatioHighBits(), 2); + + /* Write low-order 16-bits of sample rate ratio to demod register. + * Note: none of the defined rates have a low order ratio value, so we + * simply write a zero to the register */ + writeDemodRegister(Page.ONE, (short) 0xA1, 0, 2); + + /* Set sample rate correction to 0 */ + setSampleRateFrequencyCorrection(0); + + /* Reset the demod for the changes to take effect */ + writeDemodRegister(Page.ONE, (short) 0x01, 0x14, 1); + writeDemodRegister(Page.ONE, (short) 0x01, 0x10, 1); + + /* Apply any tuner specific sample rate filter settings */ + setSampleRateFilters(sampleRate.getRate()); + mSampleRate = sampleRate; + mFrequencyController.setSampleRate(sampleRate.getRate()); + } + finally + { + getLock().unlock(); } } @@ -1001,28 +971,14 @@ public abstract class RTL2832TunerController extends USBTunerController */ private int getUSBTransferBufferSize(double sampleRate) { - if(sampleRate > 300000) - { - return USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE; - } - - return USB_TRANSFER_BUFFER_SIZE_LOW_SAMPLE_RATE; + return USB_TRANSFER_BUFFER_SIZE; } public void setSampleRateFrequencyCorrection(int ppm) throws SourceException { int offset = -ppm * TWO_TO_22_POWER / 1000000; - - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x3F, - (offset & 0xFF), - 1); - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x3E, - (Integer.rotateRight(offset, 8) & 0xFF), - 1); + writeDemodRegister(Page.ONE, (short) 0x3F, (offset & 0xFF), 1); + writeDemodRegister(Page.ONE, (short) 0x3E, (Integer.rotateRight(offset, 8) & 0xFF), 1); /* Test to retune controller to apply frequency correction */ try { @@ -1036,9 +992,8 @@ public abstract class RTL2832TunerController extends USBTunerController public int getSampleRateFrequencyCorrection() throws UsbException { - int high = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x3E, 1); - int low = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x3F, 1); - + int high = readDemodRegister(Page.ONE, (short) 0x3E, 1); + int low = readDemodRegister(Page.ONE, (short) 0x3F, 1); return (Integer.rotateLeft(high, 8) | low); } @@ -1061,14 +1016,12 @@ public abstract class RTL2832TunerController extends USBTunerController * Label 3: serial * Label 4,5 ... (user defined) */ - public byte[] readEEPROM(DeviceHandle handle, short offset, int length) - throws IllegalArgumentException + public byte[] readEEPROM(short offset, int length) throws IllegalArgumentException { if(offset + length > 256) { - throw new IllegalArgumentException("cannot read more than 256 " - + "bytes from EEPROM - requested to read to byte [" + - (offset + length) + "]"); + throw new IllegalArgumentException("cannot read more than 256 bytes from EEPROM - requested read to byte [" + + (offset + length) + "]"); } byte[] data = new byte[length]; @@ -1077,28 +1030,26 @@ public abstract class RTL2832TunerController extends USBTunerController try { /* Tell the RTL-2832 to address the EEPROM */ - writeRegister(handle, Block.I2C, EEPROM_ADDRESS, (byte) offset, 1); + writeRegister(Block.I2C, EEPROM_ADDRESS, (byte) offset, 1); } catch(LibUsbException e) { - mLog.error("usb error while attempting to set read address to " - + "EEPROM register, prior to reading the EEPROM device " - + "descriptor", e); + mLog.error("usb error while attempting to set read address to EEPROM register, prior to reading the EEPROM " + + "device descriptor", e); } for(int x = 0; x < length; x++) { try { - read(handle, EEPROM_ADDRESS, Block.I2C, buffer); + read(EEPROM_ADDRESS, Block.I2C, buffer); data[x] = buffer.get(); buffer.rewind(); } catch(Exception e) { - mLog.error("error while reading eeprom byte [" + x + "/" + - length + "] aborting eeprom read and returning partially " - + "filled descriptor byte array", e); + mLog.error("error while reading eeprom byte [" + x + "/" + length + "] aborting eeprom read and " + + "returning partially filled descriptor byte array", e); x = length; } } @@ -1112,19 +1063,15 @@ public abstract class RTL2832TunerController extends USBTunerController * Note: introduce a 5 millisecond delay between each successive write to * the EEPROM or subsequent writes may fail. */ - public void writeEEPROMByte(DeviceHandle handle, byte offset, byte value) - throws IllegalArgumentException, UsbDisconnectedException, UsbException + public void writeEEPROMByte(byte offset, byte value) throws IllegalArgumentException, UsbDisconnectedException { if(offset < 0 || offset > 255) { - throw new IllegalArgumentException("RTL2832 Tuner Controller - " - + "EEPROM offset must be within range of 0 - 255"); + throw new IllegalArgumentException("RTL2832 Tuner Controller - EEPROM offset must be within range of 0 - 255"); } - int offsetAndValue = Integer.rotateLeft((0xFF & offset), 8) | - (0xFF & value); - - writeRegister(handle, Block.I2C, EEPROM_ADDRESS, offsetAndValue, 2); + int offsetAndValue = Integer.rotateLeft((0xFF & offset), 8) | (0xFF & value); + writeRegister(Block.I2C, EEPROM_ADDRESS, offsetAndValue, 2); } public enum Address @@ -1153,7 +1100,7 @@ public abstract class RTL2832TunerController extends USBTunerController private int mAddress; - private Address(int address) + Address(int address) { mAddress = address; } @@ -1172,7 +1119,7 @@ public abstract class RTL2832TunerController extends USBTunerController private int mPage; - private Page(int page) + Page(int page) { mPage = page; } @@ -1200,7 +1147,7 @@ public abstract class RTL2832TunerController extends USBTunerController private int mValue; - private Block(int value) + Block(int value) { mValue = value; } @@ -1264,7 +1211,7 @@ public abstract class RTL2832TunerController extends USBTunerController private int mRate; private String mLabel; - private SampleRate(int ratioHigh, int rate, String label) + SampleRate(int ratioHigh, int rate, String label) { mRatioHigh = ratioHigh; mRate = rate; @@ -1372,15 +1319,12 @@ public abstract class RTL2832TunerController extends USBTunerController public boolean isValid() { - return mData[0] == (byte) 0x28 && - mData[1] == (byte) 0x32; + return mData[0] != (byte)0x0 && mData[1] != (byte)0x0; } public String getVendorID() { - int id = Integer.rotateLeft((0xFF & mData[3]), 8) | - (0xFF & mData[2]); - + int id = Integer.rotateLeft((0xFF & mData[3]), 8) | (0xFF & mData[2]); return String.format("%04X", id); } @@ -1391,9 +1335,7 @@ public abstract class RTL2832TunerController extends USBTunerController public String getProductID() { - int id = Integer.rotateLeft((0xFF & mData[5]), 8) | - (0xFF & mData[4]); - + int id = Integer.rotateLeft((0xFF & mData[5]), 8) | (0xFF & mData[4]); return String.format("%04X", id); } @@ -1415,23 +1357,19 @@ public abstract class RTL2832TunerController extends USBTunerController public boolean remoteWakeupEnabled() { byte mask = (byte) 0x01; - return (mData[7] & mask) == mask; } public boolean irEnabled() { byte mask = (byte) 0x02; - return (mData[7] & mask) == mask; } private void getLabels() { mLabels.clear(); - int start = 0x09; - while(start < 256) { start = getLabel(start); @@ -1459,9 +1397,7 @@ public abstract class RTL2832TunerController extends USBTunerController /* Translate the bytes as UTF-16 Little Endian and store the label */ String label = new String(data, Charset.forName("UTF-16LE")); - mLabels.add(label); - return start + length; } @@ -1519,62 +1455,135 @@ public abstract class RTL2832TunerController extends USBTunerController } /** - * Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started. + * Adapter that provides access to the private methods of this RTL2832 USB tuner device's USB and other interfaces. + * + * Note: most of the methods exposed by this tuner are private in the tuner controller class by design to prevent + * the user from incorrectly accessing them. This private adapter exposes these as public methods that the various + * RTL2832 tuner implementations need access to. */ - @Override - public void addBufferListener(Listener listener) + public class ControllerAdapter { - boolean hasExistingListeners = hasBufferListeners(); + private RTL2832TunerController mController; - super.addBufferListener(listener); - - if(!hasExistingListeners) - { - mUSBTransferProcessor.setListener(this); - } - } - - /** - * Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners. - */ - @Override - public void removeBufferListener(Listener listener) - { - super.removeBufferListener(listener); - - if(!hasBufferListeners()) - { - mUSBTransferProcessor.removeListener(); - } - } - - /** - * RTL-2832 USB transfer processor. Extends USB transfer processor and allows resetting the device USB - * buffer prior to starting streaming. - */ - public class RTL2832USBTransferProcessor extends USBTransferProcessor - { /** - * Manages stream of USB transfer buffers and converts buffers to complex buffer samples for distribution to - * any registered listeners. - * - * @param deviceName to use when logging information or errors - * @param deviceHandle to the USB bulk transfer device - * @param nativeBufferFactory specific to the tuner's byte buffer format for converting to floating point I/Q samples - * @param bufferSize in bytes. Should be a multiple of two: 65536, 131072 or 262144. - * @param tunerErrorListener to receive error state message + * Constructs an instance + * @param controller for this adapter */ - public RTL2832USBTransferProcessor(String deviceName, DeviceHandle deviceHandle, - ByteNativeBufferFactory nativeBufferFactory, int bufferSize, - ITunerErrorListener tunerErrorListener) + public ControllerAdapter(RTL2832TunerController controller) { - super(deviceName, deviceHandle, nativeBufferFactory, bufferSize, tunerErrorListener); + mController = controller; } - @Override - protected void prepareDeviceStart() + /** + * Indicates if the tuner controller is running and that the Device Handle is active. + */ + public boolean isRunning() { - resetUSBBuffer(); + return mController.isRunning(); + } + + /** + * Device handle for the tuner controller. + * @return + */ + public DeviceHandle getDeviceHandle() + { + return mController.getDeviceHandle(); + } + + /** + * Thread lock for controlling access to configuration changes that require multiple USB control message + * sequences. + * + * @return lock. + */ + public ReentrantLock getLock() + { + return mController.getLock(); + } + + /** + * Enables the I2C repeater + */ + public void enableI2CRepeater() + { + mController.enableI2CRepeater(true); + } + + /** + * Disables the I2C repeater + */ + public void disableI2CRepeater() + { + mController.enableI2CRepeater(false); + } + + /** + * Indicates if the I2C repeater is enabled + */ + public boolean isI2CRepeaterEnabled() + { + return mController.isI2CRepeaterEnabled(); + } + + /** + * Writes to the Demod registers + * @param page value + * @param address value + * @param value to write + * @param length of the value in bytes + * @throws LibUsbException if there is an error + */ + public void writeDemodRegister(Page page, short address, int value, int length) throws LibUsbException + { + mController.writeDemodRegister(page, address, value, length); + } + + /** + * Writes to the I2C register + * @param i2CAddress address + * @param i2CRegister register + * @param value to write + * @param controlI2CRepeater true to turn on/off the I2C repeater, or false if it's already been turned on. + * @throws LibUsbException if there is an error + */ + public void writeI2CRegister(byte i2CAddress, byte i2CRegister, byte value, boolean controlI2CRepeater) + throws LibUsbException + { + mController.writeI2CRegister(i2CAddress, i2CRegister, value, controlI2CRepeater); + } + + /** + * Reads the I2C register + * @param i2CAddress address + * @param i2CRegister register + * @param controlI2CRepeater to control the I2C repeater + * @return value + * @throws LibUsbException if there is an error + */ + public int readI2CRegister(byte i2CAddress, byte i2CRegister, boolean controlI2CRepeater) throws LibUsbException + { + return mController.readI2CRegister(i2CAddress, i2CRegister, controlI2CRepeater); + } + + /** + * Reads a value + * @param address to read + * @param block at the address + * @param buffer to read the value into + * @throws LibUsbException + */ + public void read(short address, Block block, ByteBuffer buffer) throws LibUsbException + { + mController.read(address, block, buffer); + } + + /** + * Sets the intermediate frequency (IF) for the digitizer + */ + public void setIFFrequency(int frequency) + { + mController.setIFFrequency(frequency); } } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832UnknownTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832UnknownTunerEditor.java new file mode 100644 index 00000000..e0e7f3ec --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832UnknownTunerEditor.java @@ -0,0 +1,102 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.rtl; + +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.ui.TunerEditor; +import net.miginfocom.swing.MigLayout; + +import javax.swing.JLabel; +import javax.swing.JSeparator; + +/** + * Tuner editor for RTL2832 tuner that has not been started, or for an unknown tuner type + */ +public class RTL2832UnknownTunerEditor extends TunerEditor +{ + /** + * Constructs an instance + * + * @param userPreferences for starting wide-band recorders + * @param tunerManager for requesting configuration saves. + * @param discoveredTuner that is not started, or that doesn't have a recognized tuner type + */ + public RTL2832UnknownTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, + DiscoveredTuner discoveredTuner) + { + super(userPreferences, tunerManager, discoveredTuner); + init(); + tunerStatusUpdated(); + } + + private void init() + { + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][][][][][][][grow]")); + + add(new JLabel("Tuner:")); + add(getTunerIdLabel(), "wrap"); + + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); + + add(getButtonPanel(), "span,align left"); + + add(new JSeparator(), "span,growx,push"); + +// add(new JLabel("Frequency (MHz):")); +// add(getFrequencyPanel(), "wrap"); +// +// add(new JLabel("Sample Rate:")); +//// add(getSampleRateCombo(), "wrap"); +// +// add(new JSeparator(), "span,growx,push"); + } + + @Override + protected void save() + { + //No-op + } + + @Override + protected void tunerStatusUpdated() + { + setLoading(true); + getTunerIdLabel().setText(getDiscoveredTuner().getId() + (hasTuner() ? " ID:" + getTuner().getUniqueID() : "")); + + String status = getDiscoveredTuner().getTunerStatus().toString(); + if(getDiscoveredTuner().hasErrorMessage()) + { + status += " - " + getDiscoveredTuner().getErrorMessage(); + } + getTunerStatusLabel().setText(status); + getButtonPanel().updateControls(); + setLoading(false); + } + + @Override + public void setTunerLockState(boolean locked) + { + super.setTunerLockState(locked); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KEmbeddedTuner.java similarity index 58% rename from src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java rename to src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KEmbeddedTuner.java index 305ed705..dc235d5c 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KEmbeddedTuner.java @@ -21,32 +21,33 @@ package io.github.dsheirer.source.tuner.rtl.e4k; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import io.github.dsheirer.source.tuner.rtl.EmbeddedTuner; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; import javax.usb.UsbException; -import java.util.Arrays; +import java.util.EnumSet; -public class E4KTunerController extends RTL2832TunerController +/** + * Elonic E4000 Tuner + */ +public class E4KEmbeddedTuner extends EmbeddedTuner { - private final static Logger mLog = - LoggerFactory.getLogger(E4KTunerController.class); + private final static Logger mLog = LoggerFactory.getLogger(E4KEmbeddedTuner.class); - public static final long MIN_FREQUENCY = 52000000; - public static final long MAX_FREQUENCY = 2200000000l; + public static final long MINIMUM_SUPPORTED_FREQUENCY = 52000000; + public static final long MAXIMUM_SUPPORTED_FREQUENCY = 2200000000l; public static final double USABLE_BANDWIDTH_PERCENT = 0.95; public static final int DC_SPIKE_AVOID_BUFFER = 15000; - /* The local oscillator is defined by whole (integer) units of the oscillator - * frequency and fractional units representing 1/65536th of the oscillator - * frequency, meaning we can only tune the local oscillator in units of - * 439.453125 hertz. */ + /** + * The local oscillator is defined by whole (integer) units of the oscillator frequency and fractional units + * representing 1/65536th of the oscillator frequency, meaning we can only tune the local oscillator in units of + * 439.453125 hertz. + */ public static final long E4K_PLL_Y = 65536l; /* 16-bit fractional register */ public static final byte MASTER1_RESET = (byte) 0x01; public static final byte MASTER1_NORM_STBY = (byte) 0x02; @@ -54,9 +55,7 @@ public class E4KTunerController extends RTL2832TunerController public static final byte SYNTH1_PLL_LOCK = (byte) 0x01; public static final byte SYNTH1_BAND_SHIF = (byte) 0x01; - public static final byte SYNTH7_3PHASE_EN = (byte) 0x08; - public static final byte SYNTH8_VCOCAL_UPD = (byte) 0x04; public static final byte FILT3_MASK = (byte) 0x20; @@ -67,7 +66,6 @@ public class E4KTunerController extends RTL2832TunerController public static final byte IF_CHANNEL_FILTER_MASK = (byte) 0x1F; public static final byte IF_RC_FILTER_MASK = (byte) 0x0F; - public static final byte AGC1_LIN_MODE = (byte) 0x10; public static final byte AGC1_LNA_UPDATE = (byte) 0x20; public static final byte AGC1_LNA_G_LOW = (byte) 0x40; @@ -82,33 +80,14 @@ public class E4KTunerController extends RTL2832TunerController public static final byte AGC7_MIXER_GAIN_MASK = (byte) 0x01; public static final byte AGC7_MIX_GAIN_MANUAL = (byte) 0x00; public static final byte AGC7_MIX_GAIN_AUTO = (byte) 0x01; - public static final byte AGC7_GAIN_STEP_5DB = (byte) 0x20; - - public static final byte AGC8_SENS_LIN_AUTO = (byte) 0x01; - - public static final byte AGC11_LNA_GAIN_ENH = (byte) 0x01; public static final byte ENH_GAIN_MOD_MASK = (byte) 0x07; public static final byte MIXER_GAIN_MASK = (byte) 0x01; - - public static final byte DC1_CAL_REQ = (byte) 0x01; - - public static final byte DC5_I_LUT_EN = (byte) 0x01; - public static final byte DC5_Q_LUT_EN = (byte) 0x02; public static final byte DC5_RANGE_DETECTOR_ENABLED_MASK = (byte) 0x04; //DC Offset Detector Enabled public static final byte DC5_RANGE_DETECTOR_ENABLED = (byte) 0x04; //DC Offset Detector Enabled - public static final byte DC5_RANGE_EN = (byte) 0x08; - public static final byte DC5_RANGE_DETECTOR_DISABLED_MASK = (byte) 0x03; - public static final byte DC5_RANGE_DETECTOR_DISABLED = (byte) 0x00; - public static final byte DC5_TIMEVAR_EN = (byte) 0x10; - - public static final byte CLKOUT_DISABLE = (byte) 0x96; - - public static final byte CHFCALIB_CMD = (byte) 0x01; - - public E4KTunerController(Device device, DeviceDescriptor deviceDescriptor) throws SourceException + public E4KEmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter) { - super(device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); + super(adapter); } @Override @@ -117,21 +96,62 @@ public class E4KTunerController extends RTL2832TunerController return TunerType.ELONICS_E4000; } + @Override + public long getMinimumFrequencySupported() + { + return MINIMUM_SUPPORTED_FREQUENCY; + } + + @Override + public long getMaximumFrequencySupported() + { + return MAXIMUM_SUPPORTED_FREQUENCY; + } + + @Override + public int getDcSpikeHalfBandwidth() + { + return DC_SPIKE_AVOID_BUFFER; + } + + @Override + public double getUsableBandwidthPercent() + { + return USABLE_BANDWIDTH_PERCENT; + } + + @Override + public void setSamplingMode(RTL2832TunerController.SampleMode mode) throws LibUsbException + { + switch(mode) + { + case QUADRATURE: + /* Set intermediate frequency to 0 Hz */ + getAdapter().setIFFrequency(0); + + /* Enable I/Q ADC Input */ + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ZERO, (short) 0x08, (short) 0xCD, 1); + + /* Enable zero-IF mode */ + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ONE, (short) 0xB1, (short) 0x1B, 1); + + /* Set default i/q path */ + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ZERO, (short) 0x06, (short) 0x80, 1); + break; + case DIRECT: + default: + throw new LibUsbException("QUADRATURE mode is the only mode currently supported", LibUsb.ERROR_NOT_SUPPORTED); + } + } + + @Override public void apply(TunerConfiguration config) throws SourceException { - if(config != null && config instanceof E4KTunerConfiguration) + if(config instanceof E4KTunerConfiguration e4kConfig) { - E4KTunerConfiguration e4kConfig = (E4KTunerConfiguration) config; - try { - SampleRate sampleRate = e4kConfig.getSampleRate(); - setSampleRate(sampleRate); - - double correction = e4kConfig.getFrequencyCorrection(); - setFrequencyCorrection(correction); - E4KGain masterGain = e4kConfig.getMasterGain(); setGain(masterGain, true); @@ -143,137 +163,18 @@ public class E4KTunerController extends RTL2832TunerController E4KLNAGain lnaGain = e4kConfig.getLNAGain(); setLNAGain(lnaGain, true); - E4KEnhanceGain enhanceGain = e4kConfig.getEnhanceGain(); - setEnhanceGain(enhanceGain, true); - } - - try - { - setFrequency(e4kConfig.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency + IFGain ifGain = e4kConfig.getIFGain(); + setIFGain(ifGain, true); } } catch(UsbException e) { - throw new SourceException("E4KTunerController - usb error " - + "while applying tuner config", e); + throw new SourceException("Usb error while applying tuner config", e); } - } else { - throw new IllegalArgumentException("E4KTunerController - cannot " - + "apply tuner configuration of type:[" + - config.getClass() + "]"); - } - - } - - public void init() throws SourceException - { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) - { - mDeviceHandle = null; - - throw new SourceException("libusb couldn't open RTL2832 usb " - + "device [" + LibUsb.errorName(result) + "]"); - } - - claimInterface(mDeviceHandle); - - byte[] eeprom = null; - - try - { - /* Read the contents of the 256-byte EEPROM */ - eeprom = readEEPROM(mDeviceHandle, (short) 0, 256); - } - catch(Exception e) - { - mLog.error("error while reading the EEPROM device descriptor", e); - } - - try - { - mDescriptor = new Descriptor(eeprom); - - if(eeprom == null) - { - mLog.error("eeprom byte array was null - constructed " - + "empty descriptor object"); - } - } - catch(Exception e) - { - mLog.error("error while constructing device descriptor using " - + "descriptor byte array " + - (eeprom == null ? "[null]" : Arrays.toString(eeprom)), e); - } - - try - { - /* Dummy write to test USB interface */ - writeRegister(mDeviceHandle, Block.USB, - Address.USB_SYSCTL.getAddress(), (short) 0x09, 1); - - initBaseband(mDeviceHandle); - - enableI2CRepeater(mDeviceHandle, true); - - boolean i2CRepeaterControl = false; - - initTuner(i2CRepeaterControl); - - enableI2CRepeater(mDeviceHandle, false); - - try - { - setSampleRate(DEFAULT_SAMPLE_RATE); - } - catch(Exception e) - { - throw new SourceException("RTL2832 Tuner Controller - couldn't " - + "set default sample rate", e); - } - - } - catch(UsbException e) - { - e.printStackTrace(); - throw new SourceException("E4K Tuner Controller - error during " - + "init()", e); - } - - String deviceName = getTunerType().getLabel() + " " + getUniqueID(); - - mUSBTransferProcessor = new RTL2832USBTransferProcessor(deviceName, mDeviceHandle, mNativeBufferFactory, - USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE, this); - } - - @Override - public void dispose() - { - if(mDeviceHandle != null) - { - mLog.info("Releasing E4000 Tuner"); - - try - { - LibUsb.close(mDeviceHandle); - } - catch(Exception e) - { - mLog.error("error while closing device handle", e); - } - - mDeviceHandle = null; + throw new IllegalArgumentException("Unrecognized tuner config [" + config.getClass() + "]"); } } @@ -285,11 +186,11 @@ public class E4KTunerController extends RTL2832TunerController public void setSampleRateFilters(int bandwidth) throws SourceException { /* Determine repeater state so we can restore it when done */ - boolean i2CRepeaterEnabled = isI2CRepeaterEnabled(); + boolean i2CRepeaterEnabled = getAdapter().isI2CRepeaterEnabled(); if(!i2CRepeaterEnabled) { - enableI2CRepeater(mDeviceHandle, true); + getAdapter().enableI2CRepeater(); } boolean controlI2CRepeater = false; @@ -305,52 +206,46 @@ public class E4KTunerController extends RTL2832TunerController if(!i2CRepeaterEnabled) { - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } } - @Override public long getTunedFrequency() throws SourceException { try { /* Determine repeater state so we can restore it when done */ - boolean i2CRepeaterEnabled = isI2CRepeaterEnabled(); + boolean i2CRepeaterEnabled = getAdapter().isI2CRepeaterEnabled(); if(!i2CRepeaterEnabled) { - enableI2CRepeater(mDeviceHandle, true); + getAdapter().enableI2CRepeater(); } boolean controlI2CRepeater = false; - byte z = (byte) readE4KRegister(Register.SYNTH3, controlI2CRepeater); - int xHigh = readE4KRegister(Register.SYNTH4, controlI2CRepeater); int xLow = readE4KRegister(Register.SYNTH5, controlI2CRepeater); - int x = (Integer.rotateLeft(xHigh, 8)) | xLow; - int pllSetting = readE4KRegister(Register.SYNTH7, controlI2CRepeater); PLL pll = PLL.fromSetting(pllSetting); /* Return the repeater to its previous state */ if(!i2CRepeaterEnabled) { - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } return calculateActualFrequency(pll, z, x); } catch(LibUsbException e) { - throw new SourceException("E4K tuner controller - couldn't get " - + "tuned frequency", e); + throw new SourceException("E4K tuner controller - couldn't get " + "tuned frequency", e); } } @Override - public void setTunedFrequency(long frequency) throws SourceException + public synchronized void setTunedFrequency(long frequency) throws SourceException { /* Get the phase locked loop setting */ PLL pll = PLL.fromFrequency(frequency); @@ -375,61 +270,49 @@ public class E4KTunerController extends RTL2832TunerController * to rounding errors, 52 mhz becomes 51.999993, so we need to adjust * x to get 52.000003 mhz ... otherwise the PLL won't lock on the freq */ - if(actualFrequency < getMinFrequency()) + if(actualFrequency < getMinimumFrequencySupported()) { x++; actualFrequency = calculateActualFrequency(pll, z, x); } - mLock.lock(); + getAdapter().getLock().lock(); - /* Apply the actual frequency */ try { - enableI2CRepeater(mDeviceHandle, true); - + /* Apply the actual frequency */ + getAdapter().enableI2CRepeater(); boolean controlI2CRepeater = false; - /* Write the PLL setting */ writeE4KRegister(Register.SYNTH7, pll.getIndex(), controlI2CRepeater); - /* Write z (integral) value */ writeE4KRegister(Register.SYNTH3, z, controlI2CRepeater); - /* Write the x (fractional) value high-order byte to synth4 register */ writeE4KRegister(Register.SYNTH4, (byte) (x & 0xFF), controlI2CRepeater); - /* Write the x (fractional) value low-order byte to synth5 register */ - writeE4KRegister(Register.SYNTH5, - (byte) ((Integer.rotateRight(x, 8)) & 0xFF), - controlI2CRepeater); - + writeE4KRegister(Register.SYNTH5, (byte) ((Integer.rotateRight(x, 8)) & 0xFF), controlI2CRepeater); /* Set the band for the new frequency */ setBand(actualFrequency, controlI2CRepeater); - /* Set the filter */ setRFFilter(actualFrequency, controlI2CRepeater); - /* Check for PLL lock */ int lock = readE4KRegister(Register.SYNTH1, controlI2CRepeater); if(!((lock & 0x1) == 0x1)) { - throw new SourceException("E4K tuner controller - couldn't " - + "achieve PLL lock for frequency [" + - actualFrequency + "] lock value [" + lock + "]"); + throw new SourceException("E4K tuner - couldn't achieve PLL lock for frequency [" + actualFrequency + + "] lock value [" + lock + "]"); } - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } catch(UsbException e) { - throw new SourceException("E4K tuner controller - error tuning " - + "frequency [" + frequency + "]", e); + throw new SourceException("E4K tuner controller - error tuning frequency [" + frequency + "]", e); } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } @@ -438,223 +321,190 @@ public class E4KTunerController extends RTL2832TunerController long whole = pll.getScaledOscillator() * (z & 0xFF); int fractional = (int) (pll.getScaledOscillator() * - ((double) x / (double) E4K_PLL_Y)); + ((double) x / (double) E4K_PLL_Y)); return whole + fractional; } - public void initTuner(boolean controlI2CRepeater) throws UsbException + protected void initTuner() throws UsbException { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } + getAdapter().enableI2CRepeater(); boolean i2CRepeaterControl = false; /* Perform dummy read */ readE4KRegister(Register.DUMMY, i2CRepeaterControl); - - /* Reset everything and clear POR indicator */ - /* NOTE: register value remains 0010 even after we write 0111 to it */ - writeE4KRegister(Register.MASTER1, (byte) (MASTER1_RESET | - MASTER1_NORM_STBY | - MASTER1_POR_DET), - i2CRepeaterControl); - + /* Reset everything and clear POR indicator. NOTE: register value remains 0010 even after we write 0111 to it */ + writeE4KRegister(Register.MASTER1, (byte) (MASTER1_RESET | MASTER1_NORM_STBY | MASTER1_POR_DET), i2CRepeaterControl); /* Configure clock input */ writeE4KRegister(Register.CLK_INP, (byte) 0x00, i2CRepeaterControl); - /* Disable clock output */ writeE4KRegister(Register.REF_CLK, (byte) 0x00, i2CRepeaterControl); writeE4KRegister(Register.CLKOUT_PWDN, (byte) 0x96, i2CRepeaterControl); - - /* Magic Init */ + //Make the magic happen ... magicInit(i2CRepeaterControl); -// /* Set common mode voltage a bit higher for more margin - 850 mv */ -// writeMaskedE4KRegister( Register.DC7, (byte)0x7, -// (byte)0x4, i2CRepeaterControl ); -// -// /* Initialize DC offset lookup tables */ -// generateDCOffsetTables( i2CRepeaterControl ); -// -// /* Enable time variant DC correction */ -// writeE4KRegister( Register.DCTIME1, (byte)0x01, i2CRepeaterControl ); -// writeE4KRegister( Register.DCTIME2, (byte)0x01, i2CRepeaterControl ); -// + /* Initialize DC offset lookup tables */ + generateDCOffsetTables(i2CRepeaterControl); + + /* Enable time variant DC correction */ + writeE4KRegister(Register.DCTIME1, (byte) 0x01, i2CRepeaterControl); + writeE4KRegister(Register.DCTIME2, (byte) 0x01, i2CRepeaterControl); + /* Set LNA Mode */ writeE4KRegister(Register.AGC4, (byte) 0x10, i2CRepeaterControl); //High Threshold writeE4KRegister(Register.AGC5, (byte) 0x04, i2CRepeaterControl); //Low Threshold - writeE4KRegister(Register.AGC6, (byte) 0x1A, i2CRepeaterControl); //LNA calibrate + loop rate - - //TODO: lna and mixer gain were already set to manual when we generated - //the DC offset tables. Check the LNA gain setting after we set the - //LNA mode above, to see if the lna or mixer gain settings have changed - //and if not, remove the next two steps - -// /* Set LNA gain to manual using current auto LNA gain setting */ -// LNAGain lnaGain = getLNAGain( i2CRepeaterControl ); -// setLNAGain( lnaGain, i2CRepeaterControl ); -// -// /* Set Mixer gain to manual using current auto Mixer gain setting */ -// MixerGain mixerGain = getMixerGain( i2CRepeaterControl ); -// setMixerGain( mixerGain, i2CRepeaterControl ); + writeE4KRegister(Register.AGC6, (byte) 0x1A, i2CRepeaterControl); //LNA calibrate request + slowest loop rate //Temp - manual set lna mode to manual - writeMaskedE4KRegister(Register.AGC1, - AGC1_MOD_MASK, - AGCMode.SERIAL.getValue(), - false); + writeMaskedE4KRegister(Register.AGC1, AGC1_MOD_MASK, AGCMode.SERIAL.getValue(), false); //Temp - set mixer gain to manual - writeMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - AGC7_MIX_GAIN_MANUAL, - false); - - -// /* Enable enhance gain */ -// setEnhanceGain( EnhanceGain.GAIN_5, i2CRepeaterControl ); -// -// /* Enable automatic IF gain mode switching */ -// writeMaskedE4KRegister( Register.AGC8, -// IF_GAIN_MODE_SWITCHING_MASK, -// AGC8_SENS_LIN_AUTO, -// i2CRepeaterControl ); - + writeMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, AGC7_MIX_GAIN_MANUAL, false); /* Use automatic gain as a default */ setLNAGain(E4KLNAGain.AUTOMATIC, i2CRepeaterControl); setMixerGain(E4KMixerGain.AUTOMATIC, i2CRepeaterControl); - setEnhanceGain(E4KEnhanceGain.AUTOMATIC, i2CRepeaterControl); + setEnhanceGain(E4KEnhanceGain.GAIN_5, i2CRepeaterControl); //Setting of 5 is recommended in datasheet /* Set IF gain stages */ - setIFStage1Gain(IFStage1Gain.GAIN_PLUS6, i2CRepeaterControl); - setIFStage2Gain(IFStage2Gain.GAIN_PLUS0, i2CRepeaterControl); - setIFStage3Gain(IFStage3Gain.GAIN_PLUS0, i2CRepeaterControl); - setIFStage4Gain(IFStage4Gain.GAIN_PLUS0, i2CRepeaterControl); - setIFStage5Gain(IFStage5Gain.GAIN_PLUS9, i2CRepeaterControl); - setIFStage6Gain(IFStage6Gain.GAIN_PLUS9, i2CRepeaterControl); + setIFGain(IFGain.LINEARITY_8, i2CRepeaterControl); - /* Set the most narrow filter we can possible use */ + /* Set the narrowest filter we can possibly use */ setMixerFilter(MixerFilter.BW_1M9, i2CRepeaterControl); setRCFilter(RCFilter.BW_1M0, i2CRepeaterControl); setChannelFilter(ChannelFilter.BW_2M15, i2CRepeaterControl); - setChannelFilterEnabled(true, i2CRepeaterControl); -// /* Disable DC detector */ -// setDCRangeDetectorEnabled( false, i2CRepeaterControl ); + /* Disable DC detector */ + setDCRangeDetectorEnabled( false, i2CRepeaterControl ); + setDCOffsetLookupTablesEnabled(false, i2CRepeaterControl); /* Disable time variant DC correction */ writeMaskedE4KRegister(Register.DC5, (byte) 0x03, (byte) 0, i2CRepeaterControl); writeMaskedE4KRegister(Register.DCTIME1, (byte) 0x03, (byte) 0, i2CRepeaterControl); writeMaskedE4KRegister(Register.DCTIME2, (byte) 0x03, (byte) 0, i2CRepeaterControl); - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } + getAdapter().disableI2CRepeater(); } - @SuppressWarnings("unused") - private void generateDCOffsetTables(boolean controlI2CRepeater) - throws UsbException + /** + * Requests a DC offset calibration for the tuner + */ + private void requestDcOffsetCalibration(boolean controlI2CRepeater) throws LibUsbException { - boolean i2CRepeaterControl = false; + if(controlI2CRepeater) + { + getAdapter().enableI2CRepeater(); + } + + writeMaskedE4KRegister(Register.DC1, (byte) 0x1, (byte) 0x1, false); if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, true); - } - - /* Capture current gain settings to reapply at the end */ - IFStage1Gain stage1 = getIFStage1Gain(i2CRepeaterControl); - IFStage2Gain stage2 = getIFStage2Gain(i2CRepeaterControl); - IFStage3Gain stage3 = getIFStage3Gain(i2CRepeaterControl); - IFStage4Gain stage4 = getIFStage4Gain(i2CRepeaterControl); - IFStage5Gain stage5 = getIFStage5Gain(i2CRepeaterControl); - IFStage6Gain stage6 = getIFStage6Gain(i2CRepeaterControl); - - /* Set Mixer gain to manual using current auto Mixer gain setting */ - E4KMixerGain mixerGain = getMixerGain(i2CRepeaterControl); - setMixerGain(mixerGain, i2CRepeaterControl); - - /* Set LNA gain to manual using current auto LNA gain setting */ - E4KLNAGain lnaGain = getLNAGain(i2CRepeaterControl); - setLNAGain(lnaGain, i2CRepeaterControl); - - /* Set IF Stage 2 - 6 gains to maximum */ - setIFStage2Gain(IFStage2Gain.GAIN_PLUS9, i2CRepeaterControl); - setIFStage3Gain(IFStage3Gain.GAIN_PLUS9, i2CRepeaterControl); - setIFStage4Gain(IFStage4Gain.GAIN_PLUS2B, i2CRepeaterControl); - setIFStage5Gain(IFStage5Gain.GAIN_PLUS15D, i2CRepeaterControl); - setIFStage6Gain(IFStage6Gain.GAIN_PLUS15D, i2CRepeaterControl); - - /** - * Iterate all DC gain combinations, apply gain settings, read the - * DC offset value and apply it to the lookup table entry - */ - for(DCGainCombination combo : DCGainCombination.values()) - { - /* Set mixer and stage1 gain values */ - setMixerGain(combo.getMixerGain(), i2CRepeaterControl); - setIFStage1Gain(combo.getIFStage1Gain(), i2CRepeaterControl); - - /* Turn on the DC range detector */ - setDCRangeDetectorEnabled(true, i2CRepeaterControl); - - /* Calibrate the DC value */ - writeE4KRegister(Register.DC1, (byte) 0x01, i2CRepeaterControl); - - /* Get the I/Q offset and range values */ - byte offsetI = (byte) (readE4KRegister(Register.DC2, - i2CRepeaterControl) & 0x3F); - byte offsetQ = (byte) (readE4KRegister(Register.DC3, - i2CRepeaterControl) & 0x3F); - byte range = (byte) readE4KRegister(Register.DC4, - i2CRepeaterControl); - byte rangeI = (byte) (range & 0x3); - byte rangeQ = (byte) ((range >> 4) & 0x3); - - /* Write the offset and range values to the lookup table */ - writeE4KRegister(combo.getIRegister(), - (byte) (offsetI | (rangeI << 6)), - i2CRepeaterControl); - - writeE4KRegister(combo.getQRegister(), - (byte) (offsetQ | (rangeQ << 6)), - i2CRepeaterControl); - } - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - public void setDCRangeDetectorEnabled(boolean enabled, - boolean controlI2CRepeater) throws UsbException - { - if(enabled) - { - writeMaskedE4KRegister(Register.DC5, - DC5_RANGE_DETECTOR_ENABLED_MASK, - DC5_RANGE_DETECTOR_ENABLED, - controlI2CRepeater); - } - else - { - writeMaskedE4KRegister(Register.DC5, - DC5_RANGE_DETECTOR_DISABLED_MASK, - DC5_RANGE_DETECTOR_DISABLED, - controlI2CRepeater); + getAdapter().disableI2CRepeater(); } } /** - * Sets the LNA gain within the E4K Tuner + * Calibrates the DC offset lookup table values for tuner initialization + */ + private void generateDCOffsetTables(boolean controlI2CRepeater) throws UsbException + { + if(controlI2CRepeater) + { + getAdapter().enableI2CRepeater(); + } + + boolean i2CRepeaterControl = false; + + //Set all IF gain stages (1-6) to maximum values. During calibration we'll change IF stage 1 gain + setIFGain(IFGain.LINEARITY_60, i2CRepeaterControl); + + //Enable DC range detector + setDCRangeDetectorEnabled(true, i2CRepeaterControl); + + //Enable DC offset lookup tables + setDCOffsetLookupTablesEnabled(true, i2CRepeaterControl); + + //Step 0 + setIFStage1Gain(IFStage1Gain.GAIN_MINUS3, i2CRepeaterControl); + setMixerGain(E4KMixerGain.GAIN_4, i2CRepeaterControl); + requestDcOffsetCalibration(i2CRepeaterControl); + byte i = getDcOffsetI(i2CRepeaterControl); + byte q = getDcOffsetQ(i2CRepeaterControl); + writeE4KRegister(Register.ILUT0, i, i2CRepeaterControl); + writeE4KRegister(Register.QLUT0, q, i2CRepeaterControl); + + //Step 1 + setMixerGain(E4KMixerGain.GAIN_12, i2CRepeaterControl); + requestDcOffsetCalibration(i2CRepeaterControl); + i = getDcOffsetI(i2CRepeaterControl); + q = getDcOffsetQ(i2CRepeaterControl); + writeE4KRegister(Register.ILUT1, i, i2CRepeaterControl); + writeE4KRegister(Register.QLUT1, q, i2CRepeaterControl); + + //Step 2 + setIFStage1Gain(IFStage1Gain.GAIN_PLUS6, i2CRepeaterControl); + setMixerGain(E4KMixerGain.GAIN_4, i2CRepeaterControl); + requestDcOffsetCalibration(i2CRepeaterControl); + i = getDcOffsetI(i2CRepeaterControl); + q = getDcOffsetQ(i2CRepeaterControl); + writeE4KRegister(Register.ILUT2, i, i2CRepeaterControl); + writeE4KRegister(Register.QLUT2, q, i2CRepeaterControl); + + //Step 3 + setMixerGain(E4KMixerGain.GAIN_12, i2CRepeaterControl); + requestDcOffsetCalibration(i2CRepeaterControl); + i = getDcOffsetI(i2CRepeaterControl); + q = getDcOffsetQ(i2CRepeaterControl); + writeE4KRegister(Register.ILUT3, i, i2CRepeaterControl); + writeE4KRegister(Register.QLUT3, q, i2CRepeaterControl); + + if(controlI2CRepeater) + { + getAdapter().disableI2CRepeater(); + } + } + + private byte getDcOffsetI(boolean i2CRepeaterControl) + { + byte offset = (byte) (readE4KRegister(Register.DC2, i2CRepeaterControl) & 0x3F); + byte range = (byte) ((readE4KRegister(Register.DC4, i2CRepeaterControl) & 0x3) << 6); + return (byte) (range | offset); + } + + private byte getDcOffsetQ(boolean i2CRepeaterControl) + { + byte offset = (byte) (readE4KRegister(Register.DC3, i2CRepeaterControl) & 0x3F); + byte range = (byte) (readE4KRegister(Register.DC4, i2CRepeaterControl) << 2); + return (byte) (range | offset); + } + + /** + * Enables or disables the DC offset lookup tables. * + * @param enabled + * @param controlI2CRepeater + * @throws LibUsbException + */ + public void setDCOffsetLookupTablesEnabled(boolean enabled, boolean controlI2CRepeater) throws LibUsbException + { + byte value = enabled ? (byte) 0x3 : (byte) 0x0; + writeMaskedE4KRegister(Register.DC5, value, (byte) 0x3, controlI2CRepeater); + } + + /** + * Enables or disables the DC range detector + */ + public void setDCRangeDetectorEnabled(boolean enabled, boolean controlI2CRepeater) throws LibUsbException + { + byte value = enabled ? DC5_RANGE_DETECTOR_ENABLED : (byte) 0x0; + writeMaskedE4KRegister(Register.DC5, DC5_RANGE_DETECTOR_ENABLED_MASK, value, controlI2CRepeater); + } + + /** + * Sets the LNA gain within the E4K Tuner + *

* Note: requires I2C repeater enabled * * @param gain @@ -662,69 +512,49 @@ public class E4KTunerController extends RTL2832TunerController */ public void setLNAGain(E4KLNAGain gain, boolean controlI2CRepeater) throws UsbException { - mLock.lock(); + getAdapter().getLock().lock(); try { if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, true); + getAdapter().enableI2CRepeater(); } if(gain == E4KLNAGain.AUTOMATIC) { - writeMaskedE4KRegister(Register.AGC1, - AGC1_MOD_MASK, - AGCMode.IF_SERIAL_LNA_AUTON.getValue(), - false); + writeMaskedE4KRegister(Register.AGC1, AGC1_MOD_MASK, AGCMode.IF_SERIAL_LNA_AUTON.getValue(), false); } else { - writeMaskedE4KRegister(Register.AGC1, - AGC1_MOD_MASK, - AGCMode.SERIAL.getValue(), - false); - - - writeMaskedE4KRegister(E4KLNAGain.getRegister(), - E4KLNAGain.getMask(), - gain.getValue(), - false); + writeMaskedE4KRegister(Register.AGC1, AGC1_MOD_MASK, AGCMode.SERIAL.getValue(), false); + writeMaskedE4KRegister(E4KLNAGain.getRegister(), E4KLNAGain.getMask(), gain.getValue(), false); } if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } /** * Reads LNA gain from E4K tuner - * + *

* Note: requires I2C repeater enabled */ public E4KLNAGain getLNAGain(boolean controlI2CRepeater) throws UsbException { - mLock.lock(); - - try - { - return E4KLNAGain.fromRegisterValue(readE4KRegister(E4KLNAGain.getRegister(), controlI2CRepeater)); - } - finally - { - mLock.unlock(); - } + return E4KLNAGain.fromRegisterValue(readE4KRegister(E4KLNAGain.getRegister(), controlI2CRepeater)); } /** * Sets enhanced gain for E4K repeater. - * + *

* Note: requires I2C repeater enabled * * @param gain @@ -732,21 +562,12 @@ public class E4KTunerController extends RTL2832TunerController */ public void setEnhanceGain(E4KEnhanceGain gain, boolean controlI2CRepeater) throws UsbException { - mLock.lock(); - - try - { - writeMaskedE4KRegister(E4KEnhanceGain.getRegister(), E4KEnhanceGain.getMask(), gain.getValue(), controlI2CRepeater); - } - finally - { - mLock.unlock(); - } + writeMaskedE4KRegister(E4KEnhanceGain.getRegister(), E4KEnhanceGain.getMask(), gain.getValue(), controlI2CRepeater); } /** * Gets the enhanced gain setting in the E4K tuner. - * + *

* Note: requires I2C repeater * * @return @@ -754,271 +575,182 @@ public class E4KTunerController extends RTL2832TunerController */ public E4KEnhanceGain getEnhanceGain(boolean controlI2CRepeater) throws UsbException { - mLock.lock(); - - try - { - return E4KEnhanceGain.fromRegisterValue(readE4KRegister(E4KEnhanceGain.getRegister(), controlI2CRepeater)); - } - finally - { - mLock.unlock(); - } + return E4KEnhanceGain.fromRegisterValue(readE4KRegister(E4KEnhanceGain.getRegister(), controlI2CRepeater)); } + /** + * Sets the mixer gain + * + * @param gain to apply + * @param controlI2CRepeater set to true to turn on/off repeater within this method or false if already enabled. + * @throws UsbException on error + */ public void setMixerGain(E4KMixerGain gain, boolean controlI2CRepeater) throws UsbException { - mLock.lock(); + getAdapter().getLock().lock(); try { if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, true); + getAdapter().enableI2CRepeater(); } - boolean localI2CRepeaterControl = false; + boolean repeaterControl = false; if(gain == E4KMixerGain.AUTOMATIC) { - writeMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - AGC7_MIX_GAIN_AUTO, - localI2CRepeaterControl); + writeMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, AGC7_MIX_GAIN_AUTO, repeaterControl); } else { - writeMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - AGC7_MIX_GAIN_MANUAL, - localI2CRepeaterControl); - + writeMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, AGC7_MIX_GAIN_MANUAL, repeaterControl); /* Set the desired manual gain setting */ - writeMaskedE4KRegister(E4KMixerGain.getRegister(), - E4KMixerGain.getMask(), - gain.getValue(), - localI2CRepeaterControl); + writeMaskedE4KRegister(E4KMixerGain.getRegister(), E4KMixerGain.getMask(), gain.getValue(), repeaterControl); } if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } public E4KMixerGain getMixerGain(boolean controlI2CRepeater) throws UsbException { - mLock.lock(); + byte autoOrManual = readMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, controlI2CRepeater); + + if(autoOrManual == AGC7_MIX_GAIN_AUTO) + { + return E4KMixerGain.AUTOMATIC; + } + else + { + int register = readE4KRegister(E4KMixerGain.getRegister(), controlI2CRepeater); + return E4KMixerGain.fromRegisterValue(register); + } + } + + /** + * Sets IF Gains 1 - 6 using a set of linear gain values defined in the IFGain enumeration + * + * @param gain to apply + * @param controlI2CRepeater set false if I2C repeater is already enabled or true if the I2C repeater should + * be turned on and off within this method. + * @throws LibUsbException if the write operation doesn't complete successfully + */ + public void setIFGain(IFGain gain, boolean controlI2CRepeater) throws LibUsbException + { + getAdapter().getLock().lock(); try { - byte autoOrManual = readMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, controlI2CRepeater); - - if(autoOrManual == AGC7_MIX_GAIN_AUTO) - { - return E4KMixerGain.AUTOMATIC; - } - else - { - int register = readE4KRegister(E4KMixerGain.getRegister(), controlI2CRepeater); - return E4KMixerGain.fromRegisterValue(register); - } + byte linearityMode = gain.isLinearity() ? (byte) 0x00 : (byte) 0x10; + writeMaskedE4KRegister(Register.AGC1, gain.getLinearityModeMask(), linearityMode, controlI2CRepeater); + writeMaskedE4KRegister(Register.GAIN3, gain.getGain3Mask(), gain.getGain3(), controlI2CRepeater); + writeMaskedE4KRegister(Register.GAIN4, gain.getGain4Mask(), gain.getGain4(), controlI2CRepeater); } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } - public void setIFStage1Gain(IFStage1Gain gain, boolean controlI2CRepeater) throws UsbException + /** + * Sets the IF Stage 1 gain value. + */ + public void setIFStage1Gain(IFStage1Gain gain, boolean controlI2CRepeater) { - writeMaskedE4KRegister(IFStage1Gain.getRegister(), - IFStage1Gain.getMask(), - gain.getValue(), - controlI2CRepeater); + if(controlI2CRepeater) + { + getAdapter().enableI2CRepeater(); + } + + writeMaskedE4KRegister(Register.GAIN3, gain.getValue(), gain.getMask(), false); + + if(controlI2CRepeater) + { + getAdapter().disableI2CRepeater(); + } } - public IFStage1Gain getIFStage1Gain(boolean controlI2CRepeater) - throws UsbException + public void setMixerFilter(MixerFilter filter, boolean controlI2CRepeater) throws LibUsbException { - return IFStage1Gain.fromRegisterValue( - readE4KRegister(IFStage1Gain.getRegister(), controlI2CRepeater)); + writeMaskedE4KRegister(MixerFilter.getRegister(), MixerFilter.getMask(), filter.getValue(), controlI2CRepeater); } - public void setIFStage2Gain(IFStage2Gain gain, - boolean controlI2CRepeater) throws UsbException + public MixerFilter getMixerFilter(boolean controlI2CRepeater) throws UsbException { - writeMaskedE4KRegister(IFStage2Gain.getRegister(), - IFStage2Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage2Gain getIFStage2Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage2Gain.fromRegisterValue( - readE4KRegister(IFStage2Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage3Gain(IFStage3Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage3Gain.getRegister(), - IFStage3Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage3Gain getIFStage3Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage3Gain.fromRegisterValue( - readE4KRegister(IFStage3Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage4Gain(IFStage4Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage4Gain.getRegister(), - IFStage4Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage4Gain getIFStage4Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage4Gain.fromRegisterValue( - readE4KRegister(IFStage4Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage5Gain(IFStage5Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage5Gain.getRegister(), - IFStage5Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage5Gain getIFStage5Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage5Gain.fromRegisterValue( - readE4KRegister(IFStage5Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage6Gain(IFStage6Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage6Gain.getRegister(), - IFStage6Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage6Gain getIFStage6Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage6Gain.fromRegisterValue( - readE4KRegister(IFStage6Gain.getRegister(), controlI2CRepeater)); - } - - public void setMixerFilter(MixerFilter filter, - boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(MixerFilter.getRegister(), - MixerFilter.getMask(), - filter.getValue(), - controlI2CRepeater); - } - - public MixerFilter getMixerFilter(boolean controlI2CRepeater) - throws UsbException - { - int value = readE4KRegister(MixerFilter.getRegister(), - controlI2CRepeater); - + int value = readE4KRegister(MixerFilter.getRegister(), controlI2CRepeater); return MixerFilter.fromRegisterValue(value); } - public void setRCFilter(RCFilter filter, - boolean controlI2CRepeater) throws LibUsbException + public void setRCFilter(RCFilter filter, boolean controlI2CRepeater) throws LibUsbException { - writeMaskedE4KRegister(RCFilter.getRegister(), - RCFilter.getMask(), - filter.getValue(), - controlI2CRepeater); + writeMaskedE4KRegister(RCFilter.getRegister(), RCFilter.getMask(), filter.getValue(), controlI2CRepeater); } public RCFilter getRCFilter(boolean controlI2CRepeater) throws UsbException { int value = readE4KRegister(RCFilter.getRegister(), controlI2CRepeater); - return RCFilter.fromRegisterValue(value); } - public void setChannelFilter(ChannelFilter filter, - boolean controlI2CRepeater) throws LibUsbException + public void setChannelFilter(ChannelFilter filter, boolean controlI2CRepeater) throws LibUsbException { - writeMaskedE4KRegister(ChannelFilter.getRegister(), - ChannelFilter.getMask(), - filter.getValue(), - controlI2CRepeater); + writeMaskedE4KRegister(ChannelFilter.getRegister(), ChannelFilter.getMask(), filter.getValue(), controlI2CRepeater); } - public ChannelFilter getChannelFilter(boolean controlI2CRepeater) - throws UsbException + public ChannelFilter getChannelFilter(boolean controlI2CRepeater) throws UsbException { int value = readE4KRegister(ChannelFilter.getRegister(), controlI2CRepeater); - return ChannelFilter.fromRegisterValue(value); - } - public void setChannelFilterEnabled(boolean enabled, - boolean controlI2CRepeater) throws UsbException + public void setChannelFilterEnabled(boolean enabled, boolean controlI2CRepeater) throws UsbException { if(enabled) { - writeMaskedE4KRegister(Register.FILT3, - FILT3_MASK, - FILT3_ENABLE, - controlI2CRepeater); + writeMaskedE4KRegister(Register.FILT3, FILT3_MASK, FILT3_ENABLE, controlI2CRepeater); } else { - writeMaskedE4KRegister(Register.FILT3, - FILT3_MASK, - FILT3_DISABLE, - controlI2CRepeater); + writeMaskedE4KRegister(Register.FILT3, FILT3_MASK, FILT3_DISABLE, controlI2CRepeater); } } + /** + * Enables or disables VCO auto-calibration. + */ + public void setVcoAutoCalibration(boolean enabled, boolean controlI2CRepeater) + { + byte value = enabled ? (byte) 0x01 : (byte) 0x00; + byte mask = (byte) 0x3; + writeMaskedE4KRegister(Register.SYNTH8, mask, value, controlI2CRepeater); + } + public void setBand(long frequency, boolean controlI2CRepeater) throws UsbException { if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, true); + getAdapter().enableI2CRepeater(); } - Band band = Band.fromFrequency(frequency); + FrequencyBand frequencyBand = FrequencyBand.fromFrequency(frequency); /* Set the bias */ - switch(band) + switch(frequencyBand) { case UHF: case VHF2: case VHF3: writeE4KRegister(Register.BIAS, (byte) 0x3, false); break; - case L: + case L_BAND: default: writeE4KRegister(Register.BIAS, (byte) 0x0, false); break; @@ -1029,56 +761,57 @@ public class E4KTunerController extends RTL2832TunerController * Workaround - reset register (set value to 0) before writing to it, to * avoid a gap around 325-350 MHz */ - writeMaskedE4KRegister(Register.SYNTH1, Band.getMask(), (byte) 0x0, false); - writeMaskedE4KRegister(Register.SYNTH1, Band.getMask(), band.getValue(), false); + writeMaskedE4KRegister(Register.SYNTH1, FrequencyBand.getMask(), (byte) 0x0, false); + writeMaskedE4KRegister(Register.SYNTH1, FrequencyBand.getMask(), frequencyBand.getValue(), false); if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } } private void setRFFilter(long frequency, boolean controlI2CRepeater) throws UsbException { RFFilter filter = RFFilter.fromFrequency(frequency); - - writeMaskedE4KRegister(Register.FILT1, - RFFilter.getMask(), - filter.getValue(), - controlI2CRepeater); + writeMaskedE4KRegister(Register.FILT1, RFFilter.getMask(), filter.getValue(), controlI2CRepeater); } /** - * Sets the gain combination setting. - * - * Note: does not respond to the MANUAL setting. Use the manual setting - * to configure gui components to expose the individual Mixer, LNA and - * Enhance gain settings, to allow the user to change those settings - * individually. + * Sets the master gain combination setting. + * Note: does not respond to the MANUAL setting. Use the manual setting to configure gui components to expose the + * individual Mixer, LNA and Enhance gain settings, to allow the user to change those settings individually. * * @param gain - requested gain setting * @throws UsbException if there are any errors */ public void setGain(E4KGain gain, boolean controlI2CRepeater) throws UsbException { - if(gain != E4KGain.MANUAL) + getAdapter().getLock().lock(); + + try { - if(controlI2CRepeater) + if(gain != E4KGain.MANUAL) { - enableI2CRepeater(mDeviceHandle, true); - } - - boolean i2CRepeaterControl = false; - - setLNAGain(gain.getLNAGain(), i2CRepeaterControl); - setMixerGain(gain.getMixerGain(), i2CRepeaterControl); - setEnhanceGain(gain.getEnhanceGain(), i2CRepeaterControl); - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); + if(controlI2CRepeater) + { + getAdapter().enableI2CRepeater(); + } + + boolean i2CRepeaterControl = false; + + setLNAGain(gain.getLNAGain(), i2CRepeaterControl); + setMixerGain(gain.getMixerGain(), i2CRepeaterControl); + + if(controlI2CRepeater) + { + getAdapter().disableI2CRepeater(); + } } } + finally + { + getAdapter().getLock().unlock(); + } } /* @@ -1088,7 +821,7 @@ public class E4KTunerController extends RTL2832TunerController { if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, true); + getAdapter().enableI2CRepeater(); } writeE4KRegister(Register.MAGIC_1, (byte) 0x01, false); @@ -1102,63 +835,52 @@ public class E4KTunerController extends RTL2832TunerController if(controlI2CRepeater) { - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } } - private byte readMaskedE4KRegister(Register register, - byte mask, - boolean controlI2CRepeater) - throws UsbException + private byte readMaskedE4KRegister(Register register, byte mask, boolean controlI2CRepeater) throws UsbException { int temp = readE4KRegister(register, controlI2CRepeater); return (byte) (temp & mask); } - private void writeMaskedE4KRegister(Register register, - byte mask, - byte value, - boolean controlI2CRepeater) throws LibUsbException + private void writeMaskedE4KRegister(Register register, byte mask, byte value, boolean controlI2CRepeater) throws LibUsbException { int temp = readE4KRegister(register, controlI2CRepeater); /* If the register is not set to the masked value, then change it */ if((byte) (temp & mask) != value) { - writeE4KRegister(register, - (byte) ((temp & ~mask) | (value & mask)), - controlI2CRepeater); - + writeE4KRegister(register, (byte) ((temp & ~mask) | (value & mask)), controlI2CRepeater); readE4KRegister(register, controlI2CRepeater); } } - private int readE4KRegister(Register register, - boolean controlI2CRepeater) throws LibUsbException + private int readE4KRegister(Register register, boolean controlI2CRepeater) throws LibUsbException { - return readI2CRegister(mDeviceHandle, Register.I2C_REGISTER.getValue(), - register.getValue(), controlI2CRepeater); + return getAdapter().readI2CRegister(Register.I2C_REGISTER.getValue(), register.getValue(), controlI2CRepeater); } - private void writeE4KRegister(Register register, - byte value, - boolean controlI2CRepeater) throws LibUsbException + private void writeE4KRegister(Register register, byte value, boolean controlI2CRepeater) throws LibUsbException { - writeI2CRegister(mDeviceHandle, Register.I2C_REGISTER.getValue(), - register.getValue(), value, controlI2CRepeater); + getAdapter().writeI2CRegister(Register.I2C_REGISTER.getValue(), register.getValue(), value, controlI2CRepeater); } - public enum Band + /** + * Frequency bands enumeration + */ + public enum FrequencyBand { - VHF2(0), - VHF3(2), - UHF(4), - L(6); + VHF2(0), //64-108 MHz + VHF3(2), //170-240 MHz + UHF(4), //470-858 MHz + L_BAND(6); //1452-1680 MHz private int mValue; - private Band(int value) + FrequencyBand(int value) { mValue = value; } @@ -1178,7 +900,7 @@ public class E4KTunerController extends RTL2832TunerController return toString(); } - public static Band fromFrequency(long frequency) + public static FrequencyBand fromFrequency(long frequency) { if(frequency < 140000000) //140 MHz { @@ -1194,11 +916,16 @@ public class E4KTunerController extends RTL2832TunerController } else { - return L; + return L_BAND; } } } + /** + * PLL enumeration for SYNTH7 (0x0D) Register to control the 'R' VCO output divider and 3-phase mixing. + *

+ * Note: 3-phase mixing (1-bit) and R index (3-bits) are combined in the index value that is written to the register. + */ public enum PLL { PLL_72M4(0x0F, 72400000, 48, 600000, true, "72.4 MHz"), @@ -1212,32 +939,26 @@ public class E4KTunerController extends RTL2832TunerController PLL_667M0(0x02, 667000000, 6, 4800000, false, "667.0 MHz"), PLL_1200M0(0x01, 1200000000, 4, 7200000, false, "1200.0 MHz"); - private int mValue; + private int mIndex; private long mFrequency; private int mMultiplier; private int mScaledOscillator; private boolean mRequires3PhaseMixing; private String mLabel; - private PLL(int value, - long frequency, - int multiplier, - int scaledOscillator, - boolean requires3Phase, - String label) + PLL(int index, long frequency, int multiplier, int scaledOscillator, boolean requires3Phase, String label) { - mValue = value; + mIndex = index; mFrequency = frequency; mMultiplier = multiplier; mScaledOscillator = scaledOscillator; mRequires3PhaseMixing = requires3Phase; mLabel = label; - } public byte getIndex() { - return (byte) mValue; + return (byte) mIndex; } public long getFrequency() @@ -1270,8 +991,9 @@ public class E4KTunerController extends RTL2832TunerController return mLabel; } - /* Returns the PLL setting with the closest frequency that is greater - * than the frequency argument */ + /** + * Returns the PLL setting with the closest frequency that is greater than the frequency argument + */ public static PLL fromFrequency(long frequency) { for(PLL pll : values()) @@ -1307,7 +1029,7 @@ public class E4KTunerController extends RTL2832TunerController * RF filters - UHF Band. Each enum entry defines the center frequency for * the filter and the applicable frequency minimums and maximums for the * filter. - * + *

* Note: max frequency overlaps with the next minimum frequency, so valid * frequencies should be less than the maximum frequency defined for the * filter. @@ -1316,50 +1038,55 @@ public class E4KTunerController extends RTL2832TunerController { NO_FILTER(0, 0, 0), - //UHF band Filters - M360(0, 350000000, 370000000), - M380(1, 370000000, 392500000), - M405(2, 392500000, 417500000), - M425(3, 417500000, 437500000), - M450(4, 437500000, 462500000), - M475(5, 462500000, 490000000), - M505(6, 490000000, 522500000), - M540(7, 522500000, 557500000), - M575(8, 557500000, 595000000), - M615(9, 595000000, 642500000), - M670(10, 642500000, 695000000), - M720(11, 695000000, 740000000), - M760(12, 740000000, 800000000), - M840(13, 800000000, 865000000), - M890(14, 865000000, 930000000), - M970(15, 930000000, 1135000000), + //VHF-II Filters (0.0 - 140.0) Optimal (64-108) + LP268(0, MINIMUM_SUPPORTED_FREQUENCY, 86_000_000), //Values 0 - 7 are all the same + LP299(8, 86_000_000, 140_000_000), //Values 8 - 15 are all the same - //L band filters - M1300(0, 1135000000, 1310000000), - M1320(1, 1310000000, 1340000000), - M1360(2, 1340000000, 1385000000), - M1410(3, 1385000000, 1427500000), - M1445(4, 1427500000, 1452500000), - M1460(5, 1452500000, 1475000000), - M1490(6, 1475000000, 1510000000), - M1530(7, 1510000000, 1545000000), - M1560(8, 1545000000, 1575000000), - M1590(9, 1575000000, 1615000000), - M1640(10, 1615000000, 1650000000), - M1660(11, 1650000000, 1670000000), - M1680(12, 1670000000, 1690000000), - M1700(13, 1690000000, 1710000000), - M1720(14, 1710000000, 1735000000), + //VHF-III Filters (140.0 - 350.0) Optimal (170-240) + LP509(0, 140_000_000, 245_000_000), //Values 0 - 7 are all the same + LP656(8, 245_000_000, 350_000_000), //Values 8 - 15 are all the same - /* Note: max frequency is currently limited by the max integer value - * and we set the max frequency for this filter to 2147 MHz */ - M1750(15, 1735000000, 2147000000); + //UHF Filters (350.0 - 1135) Optimal (470-858) + BP360(0, 350000000, 370000000), + BP380(1, 370000000, 392500000), + BP405(2, 392500000, 417500000), + BP425(3, 417500000, 437500000), + BP450(4, 437500000, 462500000), + BP475(5, 462500000, 490000000), + BP505(6, 490000000, 522500000), + BP540(7, 522500000, 557500000), + BP575(8, 557500000, 595000000), + BP615(9, 595000000, 642500000), + BP670(10, 642500000, 695000000), + BP720(11, 695000000, 740000000), + BP760(12, 740000000, 800000000), + BP840(13, 800000000, 865000000), + BP890(14, 865000000, 930000000), + BP970(15, 930000000, 1135000000), + + //L Band Filters (1135.0 - 2147.0) Optimal (1452-1680) + BP1300(0, 1135000000, 1310000000), + BP1320(1, 1310000000, 1340000000), + BP1360(2, 1340000000, 1385000000), + BP1410(3, 1385000000, 1427500000), + BP1445(4, 1427500000, 1452500000), + BP1460(5, 1452500000, 1475000000), + BP1490(6, 1475000000, 1510000000), + BP1530(7, 1510000000, 1545000000), + BP1560(8, 1545000000, 1575000000), + BP1590(9, 1575000000, 1615000000), + BP1640(10, 1615000000, 1650000000), + BP1660(11, 1650000000, 1670000000), + BP1680(12, 1670000000, 1690000000), + BP1700(13, 1690000000, 1710000000), + BP1720(14, 1710000000, 1735000000), + BP1750(15, 1735000000, MAXIMUM_SUPPORTED_FREQUENCY); private int mValue; private long mMinFrequency; private long mMaxFrequency; - private RFFilter(int value, long minFrequency, long maxFrequency) + RFFilter(int value, long minFrequency, long maxFrequency) { mValue = value; mMinFrequency = minFrequency; @@ -1373,7 +1100,7 @@ public class E4KTunerController extends RTL2832TunerController public static byte getMask() { - return (byte) 0xF; //TODO: make this a constant + return (byte) 0xF; } public byte getValue() @@ -1398,32 +1125,66 @@ public class E4KTunerController extends RTL2832TunerController return mMaxFrequency; } + /** + * Filter ranges by frequency band + */ + public static EnumSet VHFII_FILTERS = EnumSet.of(LP268, LP299); + public static EnumSet VHFIII_FILTERS = EnumSet.of(LP509, LP656); + public static EnumSet UHF_FILTERS = EnumSet.range(BP360, BP970); + public static EnumSet LBAND_FILTERS = EnumSet.of(BP1300, BP1750); + /** * Selects the appropriate filter for the frequency * * @param frequency * @return selected filter * @throws IllegalArgumentException if the frequency is outside the - * max value (2200 MHz) + * max value (2200 MHz) */ public static RFFilter fromFrequency(long frequency) { - if(frequency < 350000000) + switch(FrequencyBand.fromFrequency(frequency)) { - return NO_FILTER; + case VHF2: + for(RFFilter filter : VHFII_FILTERS) + { + if(filter.getMinimumFrequency() <= frequency && frequency < filter.getMaximumFrequency()) + { + return filter; + } + } + break; + case VHF3: + for(RFFilter filter : VHFIII_FILTERS) + { + if(filter.getMinimumFrequency() <= frequency && frequency < filter.getMaximumFrequency()) + { + return filter; + } + } + break; + case UHF: + for(RFFilter filter : UHF_FILTERS) + { + if(filter.getMinimumFrequency() <= frequency && frequency < filter.getMaximumFrequency()) + { + return filter; + } + } + break; + case L_BAND: + default: + for(RFFilter filter : LBAND_FILTERS) + { + if(filter.getMinimumFrequency() <= frequency && frequency < filter.getMaximumFrequency()) + { + return filter; + } + } + break; } - for(RFFilter filter : values()) - { - if(filter.getMinimumFrequency() <= frequency && - frequency < filter.getMaximumFrequency()) - { - return filter; - } - } - - throw new IllegalArgumentException("E4KTunerController - " - + "cannot find RF filter for frequency [" + frequency + "]"); + return NO_FILTER; } } @@ -1445,11 +1206,7 @@ public class E4KTunerController extends RTL2832TunerController private int mMaxBandwidth; private String mLabel; - private MixerFilter(int value, - int bandwidth, - int minimumBandwidth, - int maximumBandwidth, - String label) + MixerFilter(int value, int bandwidth, int minimumBandwidth, int maximumBandwidth, String label) { mValue = value; mBandwidth = bandwidth; @@ -1503,8 +1260,7 @@ public class E4KTunerController extends RTL2832TunerController { for(MixerFilter filter : values()) { - if(filter.getMinimumBandwidth() <= bandwidth && - bandwidth < filter.getMaximumBandwidth()) + if(filter.getMinimumBandwidth() <= bandwidth && bandwidth < filter.getMaximumBandwidth()) { return filter; } @@ -1526,8 +1282,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController - " - + "unrecognized mixer filter value [" + value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized mixer filter value [" + value + "]"); } } @@ -1556,11 +1311,7 @@ public class E4KTunerController extends RTL2832TunerController private int mMaxBandwidth; private String mLabel; - private RCFilter(int value, - int bandwidth, - int minimumBandwidth, - int maximumBandwidth, - String label) + RCFilter(int value, int bandwidth, int minimumBandwidth, int maximumBandwidth, String label) { mValue = value; mBandwidth = bandwidth; @@ -1614,8 +1365,7 @@ public class E4KTunerController extends RTL2832TunerController { for(RCFilter filter : values()) { - if(filter.getMinimumBandwidth() <= bandwidth && - bandwidth < filter.getMaximumBandwidth()) + if(filter.getMinimumBandwidth() <= bandwidth && bandwidth < filter.getMaximumBandwidth()) { return filter; } @@ -1637,45 +1387,44 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController - " - + "unrecognized rc filter value [" + value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized rc filter value [" + value + "]"); } } public enum ChannelFilter { - BW_5M50(0x00, 5500000, 5400000, 28800000, "5.50 MHz"), - BW_5M30(0x01, 5300000, 5150000, 5400000, "5.30 MHz"), - BW_5M00(0x02, 5000000, 4900000, 5150000, "5.00 MHz"), - BW_4M80(0x03, 4800000, 4700000, 4900000, "4.80 MHz"), - BW_4M60(0x04, 4600000, 4500000, 4700000, "4.60 MHz"), - BW_4M40(0x05, 4400000, 4350000, 4500000, "4.40 MHz"), - BW_4M30(0x06, 4300000, 4200000, 4350000, "4.30 MHz"), - BW_4M10(0x07, 4100000, 4000000, 4200000, "4.10 MHz"), - BW_3M90(0x08, 3900000, 3850000, 4000000, "3.90 MHz"), - BW_3M80(0x09, 3800000, 3750000, 3850000, "3.80 MHz"), - BW_3M70(0x0A, 3700000, 3650000, 3750000, "3.70 MHz"), - BW_3M60(0x0B, 3600000, 3500000, 3650000, "3.60 MHz"), - BW_3M40(0x0C, 3400000, 3350000, 3500000, "3.40 MHz"), - BW_3M30(0x0D, 3300000, 3250000, 3350000, "3.30 MHz"), - BW_3M20(0x0E, 3200000, 3150000, 3250000, "3.20 MHz"), - BW_3M10(0x0F, 3100000, 3050000, 3150000, "3.10 MHz"), - BW_3M00(0x10, 3000000, 2975000, 3050000, "3.00 MHz"), - BW_2M95(0x11, 2950000, 2925000, 2975000, "2.95 MHz"), - BW_2M90(0x12, 2900000, 2850000, 2925000, "2.90 MHz"), - BW_2M80(0x13, 2800000, 2775000, 2850000, "2.80 MHz"), - BW_2M75(0x14, 2750000, 2750000, 2775000, "2.75 MHz"), - BW_2M70(0x15, 2700000, 2650000, 2750000, "2.70 MHz"), - BW_2M60(0x16, 2600000, 2575000, 2650000, "2.60 MHz"), - BW_2M55(0x17, 2550000, 2525000, 2575000, "2.55 MHz"), - BW_2M50(0x18, 2500000, 2475000, 2525000, "2.50 MHz"), - BW_2M45(0x19, 2450000, 2425000, 2475000, "2.45 MHz"), - BW_2M40(0x1A, 2400000, 2350000, 2425000, "2.40 MHz"), - BW_2M30(0x1B, 2300000, 2290000, 2350000, "2.30 MHz"), - BW_2M28(0x1C, 2280000, 2260000, 2290000, "2.28 MHz"), - BW_2M24(0x1D, 2240000, 2220000, 2260000, "2.24 MHz"), - BW_2M20(0x1E, 2200000, 2175000, 2220000, "2.20 MHz"), - BW_2M15(0x1F, 2150000, 0, 2175000, "2.15 MHz"); + BW_5M50(0x00, 5500000, 5500000, 28800000, "5.50 MHz"), + BW_5M30(0x01, 5300000, 5300000, 5500000, "5.30 MHz"), + BW_5M00(0x02, 5000000, 5000000, 5300000, "5.00 MHz"), + BW_4M80(0x03, 4800000, 4800000, 5000000, "4.80 MHz"), + BW_4M60(0x04, 4600000, 4600000, 4800000, "4.60 MHz"), + BW_4M40(0x05, 4400000, 4400000, 4600000, "4.40 MHz"), + BW_4M30(0x06, 4300000, 4300000, 4400000, "4.30 MHz"), + BW_4M10(0x07, 4100000, 4100000, 4300000, "4.10 MHz"), + BW_3M90(0x08, 3900000, 3900000, 4100000, "3.90 MHz"), + BW_3M80(0x09, 3800000, 3800000, 3900000, "3.80 MHz"), + BW_3M70(0x0A, 3700000, 3700000, 3800000, "3.70 MHz"), + BW_3M60(0x0B, 3600000, 3600000, 3700000, "3.60 MHz"), + BW_3M40(0x0C, 3400000, 3400000, 3600000, "3.40 MHz"), + BW_3M30(0x0D, 3300000, 3300000, 3400000, "3.30 MHz"), + BW_3M20(0x0E, 3200000, 3200000, 3300000, "3.20 MHz"), + BW_3M10(0x0F, 3100000, 3100000, 3200000, "3.10 MHz"), + BW_3M00(0x10, 3000000, 3000000, 3100000, "3.00 MHz"), + BW_2M95(0x11, 2950000, 2950000, 3000000, "2.95 MHz"), + BW_2M90(0x12, 2900000, 2900000, 2950000, "2.90 MHz"), + BW_2M80(0x13, 2800000, 2800000, 2900000, "2.80 MHz"), + BW_2M75(0x14, 2750000, 2750000, 2800000, "2.75 MHz"), + BW_2M70(0x15, 2700000, 2700000, 2750000, "2.70 MHz"), + BW_2M60(0x16, 2600000, 2600000, 2700000, "2.60 MHz"), + BW_2M55(0x17, 2550000, 2550000, 2600000, "2.55 MHz"), + BW_2M50(0x18, 2500000, 2500000, 2550000, "2.50 MHz"), + BW_2M45(0x19, 2450000, 2450000, 2500000, "2.45 MHz"), + BW_2M40(0x1A, 2400000, 2400000, 2450000, "2.40 MHz"), + BW_2M30(0x1B, 2300000, 2300000, 2400000, "2.30 MHz"), + BW_2M28(0x1C, 2280000, 2280000, 2300000, "2.28 MHz"), + BW_2M24(0x1D, 2240000, 2240000, 2280000, "2.24 MHz"), + BW_2M20(0x1E, 2200000, 2200000, 2240000, "2.20 MHz"), + BW_2M15(0x1F, 2150000, 0, 2150000, "2.15 MHz"); private int mValue; private int mBandwidth; @@ -1683,11 +1432,7 @@ public class E4KTunerController extends RTL2832TunerController private int mMaxBandwidth; private String mLabel; - private ChannelFilter(int value, - int bandwidth, - int minimumBandwidth, - int maximumBandwidth, - String label) + ChannelFilter(int value, int bandwidth, int minimumBandwidth, int maximumBandwidth, String label) { mValue = value; mBandwidth = bandwidth; @@ -1741,8 +1486,7 @@ public class E4KTunerController extends RTL2832TunerController { for(ChannelFilter filter : values()) { - if(filter.getMinimumBandwidth() <= bandwidth && - bandwidth < filter.getMaximumBandwidth()) + if(filter.getMinimumBandwidth() <= bandwidth && bandwidth < filter.getMaximumBandwidth()) { return filter; } @@ -1764,8 +1508,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController - " - + "unrecognized channel filter value [" + value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized channel filter value [" + value + "]"); } } @@ -1789,7 +1532,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private E4KLNAGain(int value, String label) + E4KLNAGain(int value, String label) { mValue = value; mLabel = label; @@ -1832,51 +1575,44 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized LNA Gain value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized LNA Gain value [" + value + "]"); } } /** * Defines the default gain settings that are exposed to the user. - * + *

* Manual is an override that keeps each of the settings intact and allows * the user to change each of them individually */ public enum E4KGain { - AUTOMATIC("Auto", E4KMixerGain.AUTOMATIC, E4KLNAGain.AUTOMATIC, E4KEnhanceGain.AUTOMATIC), - MANUAL("Manual", null, null, null), - MINUS_10("-1.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_50, E4KEnhanceGain.AUTOMATIC), - PLUS_15("1.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_25, E4KEnhanceGain.AUTOMATIC), - PLUS_40("4.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_0, E4KEnhanceGain.AUTOMATIC), - PLUS_65("6.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_25, E4KEnhanceGain.AUTOMATIC), - PLUS_90("9.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_50, E4KEnhanceGain.AUTOMATIC), - PLUS_115("11.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_75, E4KEnhanceGain.AUTOMATIC), - PLUS_140("14.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_100, E4KEnhanceGain.AUTOMATIC), - PLUS_165("16.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_125, E4KEnhanceGain.AUTOMATIC), - PLUS_190("19.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_150, E4KEnhanceGain.AUTOMATIC), - PLUS_215("21.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_175, E4KEnhanceGain.AUTOMATIC), - PLUS_240("24.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_200, E4KEnhanceGain.AUTOMATIC), - PLUS_290("29.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_250, E4KEnhanceGain.AUTOMATIC), - PLUS_340("34.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_300, E4KEnhanceGain.AUTOMATIC), - PLUS_420("42.0 db", E4KMixerGain.GAIN_12, E4KLNAGain.GAIN_PLUS_300, E4KEnhanceGain.AUTOMATIC); + AUTOMATIC("Auto", E4KMixerGain.AUTOMATIC, E4KLNAGain.AUTOMATIC), + MANUAL("Manual", null, null), + MINUS_10("-1.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_50), + PLUS_15("1.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_25), + PLUS_40("4.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_0), + PLUS_65("6.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_25), + PLUS_90("9.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_50), + PLUS_115("11.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_75), + PLUS_140("14.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_100), + PLUS_165("16.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_125), + PLUS_190("19.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_150), + PLUS_215("21.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_175), + PLUS_240("24.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_200), + PLUS_290("29.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_250), + PLUS_340("34.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_300), + PLUS_420("42.0 db", E4KMixerGain.GAIN_12, E4KLNAGain.GAIN_PLUS_300); private String mLabel; private E4KMixerGain mMixerGain; private E4KLNAGain mLNAGain; - private E4KEnhanceGain mEnhanceGain; - private E4KGain(String label, - E4KMixerGain mixerGain, - E4KLNAGain lnaGain, - E4KEnhanceGain enhanceGain) + E4KGain(String label, E4KMixerGain mixerGain, E4KLNAGain lnaGain) { mLabel = label; mMixerGain = mixerGain; mLNAGain = lnaGain; - mEnhanceGain = enhanceGain; } public String getLabel() @@ -1898,11 +1634,6 @@ public class E4KTunerController extends RTL2832TunerController { return mLNAGain; } - - public E4KEnhanceGain getEnhanceGain() - { - return mEnhanceGain; - } } public enum E4KEnhanceGain @@ -1910,13 +1641,13 @@ public class E4KTunerController extends RTL2832TunerController AUTOMATIC(0, "Auto"), GAIN_1(1, "10 db"), GAIN_3(3, "30 db"), - GAIN_5(5, "50 db"), + GAIN_5(5, "50 db"), //Recommended GAIN_7(7, "70 db"); private int mValue; private String mLabel; - private E4KEnhanceGain(int value, String label) + E4KEnhanceGain(int value, String label) { mValue = value; mLabel = label; @@ -1959,40 +1690,23 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized Enhance Gain value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized Enhance Gain value [" + value + "]"); } } public enum DCGainCombination { - LOOKUP_TABLE_0(Register.QLUT0, - Register.ILUT0, - E4KMixerGain.GAIN_4, - IFStage1Gain.GAIN_MINUS3), - LOOKUP_TABLE_1(Register.QLUT1, - Register.ILUT1, - E4KMixerGain.GAIN_4, - IFStage1Gain.GAIN_PLUS6), - LOOKUP_TABLE_2(Register.QLUT2, - Register.ILUT2, - E4KMixerGain.GAIN_12, - IFStage1Gain.GAIN_MINUS3), - LOOKUP_TABLE_3(Register.QLUT3, - Register.ILUT3, - E4KMixerGain.GAIN_12, - IFStage1Gain.GAIN_PLUS6); + LOOKUP_TABLE_0(Register.QLUT0, Register.ILUT0, E4KMixerGain.GAIN_4, IFStage1Gain.GAIN_MINUS3), + LOOKUP_TABLE_1(Register.QLUT1, Register.ILUT1, E4KMixerGain.GAIN_4, IFStage1Gain.GAIN_PLUS6), + LOOKUP_TABLE_2(Register.QLUT2, Register.ILUT2, E4KMixerGain.GAIN_12, IFStage1Gain.GAIN_MINUS3), + LOOKUP_TABLE_3(Register.QLUT3, Register.ILUT3, E4KMixerGain.GAIN_12, IFStage1Gain.GAIN_PLUS6); private Register mQRegister; private Register mIRegister; private E4KMixerGain mMixerGain; private IFStage1Gain mIFStage1Gain; - private DCGainCombination(Register qRegister, - Register iRegister, - E4KMixerGain mixer, - IFStage1Gain stage1) + DCGainCombination(Register qRegister, Register iRegister, E4KMixerGain mixer, IFStage1Gain stage1) { mQRegister = qRegister; mIRegister = iRegister; @@ -2031,7 +1745,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private E4KMixerGain(int value, String label) + E4KMixerGain(int value, String label) { mValue = value; mLabel = label; @@ -2074,9 +1788,205 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized Mixer Gain value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized Mixer Gain value [" + value + "]"); + } + } + + /** + * Combines IF Gains 1 - 4 (register 0x16h GAIN3) and IF Gains 5 - 6 (register 0x17h GAIN4) into a contiguous + * range if IF gains, per data sheet table page 38, for both Linearity and Sensitivity modes. + */ + public enum IFGain + { + LINEARITY_6((byte) 0x00, (byte) 0x00, "6"), + LINEARITY_7((byte) 0x20, (byte) 0x00, "7"), + LINEARITY_8((byte) 0x40, (byte) 0x00, "8"), + LINEARITY_9((byte) 0x00, (byte) 0x08, "9"), + LINEARITY_10((byte) 0x20, (byte) 0x08, "10"), + LINEARITY_11((byte) 0x40, (byte) 0x08, "11"), + LINEARITY_12((byte) 0x00, (byte) 0x10, "12"), + LINEARITY_13((byte) 0x20, (byte) 0x10, "13"), + LINEARITY_14((byte) 0x40, (byte) 0x10, "14"), + LINEARITY_15((byte) 0x00, (byte) 0x18, "15"), + LINEARITY_16((byte) 0x20, (byte) 0x18, "16"), + LINEARITY_17((byte) 0x40, (byte) 0x18, "17"), + LINEARITY_18((byte) 0x00, (byte) 0x20, "18"), + LINEARITY_19((byte) 0x20, (byte) 0x20, "19"), + LINEARITY_20((byte) 0x40, (byte) 0x20, "20"), + LINEARITY_21((byte) 0x00, (byte) 0x21, "21"), + LINEARITY_22((byte) 0x20, (byte) 0x21, "22"), + LINEARITY_23((byte) 0x40, (byte) 0x21, "23"), + LINEARITY_24((byte) 0x00, (byte) 0x22, "24"), + LINEARITY_25((byte) 0x20, (byte) 0x22, "25"), + LINEARITY_26((byte) 0x40, (byte) 0x22, "26"), + LINEARITY_27((byte) 0x00, (byte) 0x23, "27"), + LINEARITY_28((byte) 0x20, (byte) 0x23, "28"), + LINEARITY_29((byte) 0x40, (byte) 0x23, "29"), + LINEARITY_30((byte) 0x00, (byte) 0x24, "30"), + LINEARITY_31((byte) 0x20, (byte) 0x24, "31"), + LINEARITY_32((byte) 0x40, (byte) 0x24, "32"), + LINEARITY_33((byte) 0x08, (byte) 0x24, "33"), + LINEARITY_34((byte) 0x28, (byte) 0x24, "34"), + LINEARITY_35((byte) 0x48, (byte) 0x24, "35"), + LINEARITY_36((byte) 0x10, (byte) 0x24, "36"), + LINEARITY_37((byte) 0x30, (byte) 0x24, "37"), + LINEARITY_38((byte) 0x50, (byte) 0x24, "38"), + LINEARITY_39((byte) 0x18, (byte) 0x24, "39"), + LINEARITY_40((byte) 0x38, (byte) 0x24, "40"), + LINEARITY_41((byte) 0x58, (byte) 0x24, "41"), + LINEARITY_42((byte) 0x1A, (byte) 0x24, "42"), + LINEARITY_43((byte) 0x3A, (byte) 0x24, "43"), + LINEARITY_44((byte) 0x5A, (byte) 0x24, "44"), + LINEARITY_45((byte) 0x1C, (byte) 0x24, "45"), + LINEARITY_46((byte) 0x3C, (byte) 0x24, "46"), + LINEARITY_47((byte) 0x5C, (byte) 0x24, "47"), + LINEARITY_48((byte) 0x19, (byte) 0x24, "48"), + LINEARITY_49((byte) 0x39, (byte) 0x24, "49"), + LINEARITY_50((byte) 0x59, (byte) 0x24, "50"), + LINEARITY_51((byte) 0x1B, (byte) 0x24, "51"), + LINEARITY_52((byte) 0x3B, (byte) 0x24, "52"), + LINEARITY_53((byte) 0x5B, (byte) 0x24, "53"), + LINEARITY_54((byte) 0x1D, (byte) 0x24, "54"), + LINEARITY_55((byte) 0x3D, (byte) 0x24, "55"), + LINEARITY_56((byte) 0x5D, (byte) 0x24, "56"), + LINEARITY_57((byte) 0x1F, (byte) 0x24, "57"), + LINEARITY_58((byte) 0x3F, (byte) 0x24, "58"), + LINEARITY_59((byte) 0x5F, (byte) 0x24, "59"), + LINEARITY_60((byte) 0x7F, (byte) 0x24, "60"), + + SENSITIVITY_6((byte) 0x00, (byte) 0x00, "6"), + SENSITIVITY_7((byte) 0x20, (byte) 0x00, "7"), + SENSITIVITY_8((byte) 0x40, (byte) 0x00, "8"), + SENSITIVITY_9((byte) 0x02, (byte) 0x00, "9"), + SENSITIVITY_10((byte) 0x22, (byte) 0x00, "10"), + SENSITIVITY_11((byte) 0x44, (byte) 0x00, "11"), + SENSITIVITY_12((byte) 0x04, (byte) 0x00, "12"), + SENSITIVITY_13((byte) 0x24, (byte) 0x00, "13"), + SENSITIVITY_14((byte) 0x44, (byte) 0x00, "14"), + SENSITIVITY_15((byte) 0x01, (byte) 0x00, "15"), + SENSITIVITY_16((byte) 0x21, (byte) 0x00, "16"), + SENSITIVITY_17((byte) 0x41, (byte) 0x00, "17"), + SENSITIVITY_18((byte) 0x03, (byte) 0x00, "18"), + SENSITIVITY_19((byte) 0x23, (byte) 0x00, "19"), + SENSITIVITY_20((byte) 0x43, (byte) 0x00, "20"), + SENSITIVITY_21((byte) 0x05, (byte) 0x00, "21"), + SENSITIVITY_22((byte) 0x25, (byte) 0x00, "22"), + SENSITIVITY_23((byte) 0x45, (byte) 0x00, "23"), + SENSITIVITY_24((byte) 0x07, (byte) 0x00, "24"), + SENSITIVITY_25((byte) 0x27, (byte) 0x00, "25"), + SENSITIVITY_26((byte) 0x47, (byte) 0x00, "26"), + SENSITIVITY_27((byte) 0x0F, (byte) 0x00, "27"), + SENSITIVITY_28((byte) 0x2F, (byte) 0x00, "28"), + SENSITIVITY_29((byte) 0x4F, (byte) 0x00, "29"), + SENSITIVITY_30((byte) 0x17, (byte) 0x00, "30"), + SENSITIVITY_31((byte) 0x37, (byte) 0x00, "31"), + SENSITIVITY_32((byte) 0x57, (byte) 0x00, "32"), + SENSITIVITY_33((byte) 0x1F, (byte) 0x00, "33"), + SENSITIVITY_34((byte) 0x3F, (byte) 0x00, "34"), + SENSITIVITY_35((byte) 0x5F, (byte) 0x00, "35"), + SENSITIVITY_36((byte) 0x1F, (byte) 0x01, "36"), + SENSITIVITY_37((byte) 0x3F, (byte) 0x01, "37"), + SENSITIVITY_38((byte) 0x5F, (byte) 0x01, "38"), + SENSITIVITY_39((byte) 0x1F, (byte) 0x02, "39"), + SENSITIVITY_40((byte) 0x3F, (byte) 0x02, "40"), + SENSITIVITY_41((byte) 0x5F, (byte) 0x02, "41"), + SENSITIVITY_42((byte) 0x1F, (byte) 0x03, "42"), + SENSITIVITY_43((byte) 0x3F, (byte) 0x03, "43"), + SENSITIVITY_44((byte) 0x5F, (byte) 0x03, "44"), + SENSITIVITY_45((byte) 0x1F, (byte) 0x04, "45"), + SENSITIVITY_46((byte) 0x3F, (byte) 0x04, "46"), + SENSITIVITY_47((byte) 0x5F, (byte) 0x04, "47"), + SENSITIVITY_48((byte) 0x1F, (byte) 0x0C, "48"), + SENSITIVITY_49((byte) 0x3F, (byte) 0x0C, "49"), + SENSITIVITY_50((byte) 0x5F, (byte) 0x0C, "50"), + SENSITIVITY_51((byte) 0x1F, (byte) 0x14, "51"), + SENSITIVITY_52((byte) 0x3F, (byte) 0x14, "52"), + SENSITIVITY_53((byte) 0x5F, (byte) 0x14, "53"), + SENSITIVITY_54((byte) 0x1F, (byte) 0x1C, "54"), + SENSITIVITY_55((byte) 0x3F, (byte) 0x1C, "55"), + SENSITIVITY_56((byte) 0x5F, (byte) 0x1C, "56"), + SENSITIVITY_57((byte) 0x1F, (byte) 0x24, "57"), + SENSITIVITY_58((byte) 0x3F, (byte) 0x24, "58"), + SENSITIVITY_59((byte) 0x5F, (byte) 0x24, "59"), + SENSITIVITY_60((byte) 0x7F, (byte) 0x24, "60"); + + private byte mGain3; + private byte mGain4; + private String mLabel; + + IFGain(byte gain3, byte gain4, String label) + { + mGain3 = gain3; + mGain4 = gain4; + mLabel = label; + } + + /** + * Value to apply to AGC3 register + */ + public byte getGain3() + { + return mGain3; + } + + /** + * Value to apply to AGC4 register + */ + public byte getGain4() + { + return mGain4; + } + + /** + * Register mask for AGC3 + */ + public byte getGain3Mask() + { + return (byte) 0x7F; + } + + /** + * Register mask for AGC4 + */ + public byte getGain4Mask() + { + return (byte) 0x3F; + } + + /** + * Linearity mode mask for AGC1 (0x1A) register + */ + public byte getLinearityModeMask() + { + return (byte) 0x10; + } + + /** + * Set of linearity gains + */ + public static EnumSet LINEARITY_GAINS = EnumSet.range(LINEARITY_6, LINEARITY_60); + + /** + * Indicates if this is a linearity or sensitivity mode + * + * @return true if linearity or false if sensitivity mode + */ + public boolean isLinearity() + { + return LINEARITY_GAINS.contains(this); + } + + @Override + public String toString() + { + if(isLinearity()) + { + return "Linearity " + mLabel + " dB"; + } + else + { + return "Sensitivity " + mLabel + " dB"; + } } } @@ -2088,7 +1998,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private IFStage1Gain(int value, String label) + IFStage1Gain(int value, String label) { mValue = value; mLabel = label; @@ -2099,7 +2009,7 @@ public class E4KTunerController extends RTL2832TunerController return Register.GAIN3; } - public static byte getMask() + public byte getMask() { return (byte) 0x1; } @@ -2118,23 +2028,6 @@ public class E4KTunerController extends RTL2832TunerController { return mLabel; } - - public static IFStage1Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage1Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 1 value [" + - value + "]"); - } } public enum IFStage2Gain @@ -2147,7 +2040,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private IFStage2Gain(int value, String label) + IFStage2Gain(int value, String label) { mValue = value; mLabel = label; @@ -2190,9 +2083,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 2 value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized IF Gain Stage 2 value [" + value + "]"); } } @@ -2206,7 +2097,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private IFStage3Gain(int value, String label) + IFStage3Gain(int value, String label) { mValue = value; mLabel = label; @@ -2249,9 +2140,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 3 value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized IF Gain Stage 3 value [" + value + "]"); } } @@ -2265,7 +2154,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private IFStage4Gain(int value, String label) + IFStage4Gain(int value, String label) { mValue = value; mLabel = label; @@ -2308,9 +2197,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 4 value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized IF Gain Stage 4 value [" + value + "]"); } } @@ -2328,7 +2215,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private IFStage5Gain(int value, String label) + IFStage5Gain(int value, String label) { mValue = value; mLabel = label; @@ -2371,9 +2258,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 5 value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized IF Gain Stage 5 value [" + value + "]"); } } @@ -2392,7 +2277,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; private String mLabel; - private IFStage6Gain(int value, String label) + IFStage6Gain(int value, String label) { mValue = value; mLabel = label; @@ -2435,9 +2320,7 @@ public class E4KTunerController extends RTL2832TunerController } } - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 6 value [" + - value + "]"); + throw new IllegalArgumentException("E4KTunerController - unrecognized IF Gain Stage 6 value [" + value + "]"); } } @@ -2449,7 +2332,7 @@ public class E4KTunerController extends RTL2832TunerController private String mLabel; - private IFFilter(String label) + IFFilter(String label) { mLabel = label; } @@ -2476,7 +2359,7 @@ public class E4KTunerController extends RTL2832TunerController private byte mValue; - private AGCMode(byte value) + AGCMode(byte value) { mValue = value; } @@ -2563,7 +2446,7 @@ public class E4KTunerController extends RTL2832TunerController private int mValue; - private Register(int value) + Register(int value) { mValue = value; } diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerConfiguration.java index 85425729..74970eab 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerConfiguration.java @@ -1,39 +1,41 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.rtl.e4k; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KGain; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; +import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KGain; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KLNAGain; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.IFGain; -public class E4KTunerConfiguration extends TunerConfiguration +/** + * RTL-2832 with embedded E4000 tuner configuration + */ +public class E4KTunerConfiguration extends RTL2832TunerConfiguration { private E4KGain mMasterGain = E4KGain.MANUAL; private E4KMixerGain mMixerGain = E4KMixerGain.GAIN_4; private E4KLNAGain mLNAGain = E4KLNAGain.GAIN_PLUS_200; - private E4KEnhanceGain mEnhanceGain = E4KEnhanceGain.GAIN_3; - private double mFrequencyCorrection = 0.0d; - private RTL2832TunerController.SampleRate mSampleRate = RTL2832TunerController.SampleRate.RATE_2_400MHZ; + private IFGain mIFGain = IFGain.LINEARITY_8; /** * Default constructor for JAXB @@ -42,18 +44,23 @@ public class E4KTunerConfiguration extends TunerConfiguration { } - public E4KTunerConfiguration(String uniqueID, String name) - { - super(uniqueID, name); - } - - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @JsonIgnore @Override public TunerType getTunerType() { return TunerType.ELONICS_E4000; } + /** + * Constructs an instance + * + * @param uniqueID for the tuner + */ + public E4KTunerConfiguration(String uniqueID) + { + super(uniqueID); + } + @JacksonXmlProperty(isAttribute = true, localName = "master_gain") public E4KGain getMasterGain() { @@ -87,37 +94,14 @@ public class E4KTunerConfiguration extends TunerConfiguration mLNAGain = lnaGain; } - @JacksonXmlProperty(isAttribute = true, localName = "enhance_gain") - public E4KEnhanceGain getEnhanceGain() + @JacksonXmlProperty(isAttribute = true, localName = "if_gain") + public IFGain getIFGain() { - return mEnhanceGain; + return mIFGain; } - public void setEnhanceGain(E4KEnhanceGain enhanceGain) + public void setIFGain(IFGain ifGain) { - mEnhanceGain = enhanceGain; + mIFGain = ifGain; } - - @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection(double value) - { - mFrequencyCorrection = value; - } - - @JacksonXmlProperty(isAttribute = true, localName = "sample_rate") - public RTL2832TunerController.SampleRate getSampleRate() - { - return mSampleRate; - } - - public void setSampleRate(RTL2832TunerController.SampleRate sampleRate) - { - mSampleRate = sampleRate; - } - } diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java index bcb55c0c..60ee7bc4 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -1,32 +1,34 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.rtl.e4k; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KGain; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; -import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KGain; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KLNAGain; +import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain; +import io.github.dsheirer.source.tuner.ui.TunerEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,51 +39,47 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import javax.usb.UsbException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; -public class E4KTunerEditor extends TunerConfigurationEditor +/** + * E4000 tuner editor + */ +public class E4KTunerEditor extends TunerEditor { private final static Logger mLog = LoggerFactory.getLogger(E4KTunerEditor.class); - private static final long serialVersionUID = 1L; + private JButton mTunerInfoButton; + private JComboBox mSampleRateCombo; + private JComboBox mMasterGainCombo; + private JComboBox mMixerGainCombo; + private JComboBox mLNAGainCombo; + private JComboBox mIfGainCombo; - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboEnhanceGain; - - private E4KTunerController mController; - private boolean mLoading; - - public E4KTunerEditor(TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner) + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager to save configuration + * @param discoveredTuner to control + */ + public E4KTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) { - super(tunerConfigurationModel); - mController = (E4KTunerController)tuner.getController(); - + super(userPreferences, tunerManager, discoveredTuner); init(); + tunerStatusUpdated(); } - private E4KTunerConfiguration getConfiguration() + /** + * Access to the E4000 embedded tuner + * @return E4000 tuner if there is a tuner, or null otherwise + */ + public E4KEmbeddedTuner getEmbeddedTuner() { - if(hasItem()) + if(hasTuner()) { - return (E4KTunerConfiguration)getItem(); + return (E4KEmbeddedTuner)getTuner().getController().getEmbeddedTuner(); } return null; @@ -89,336 +87,328 @@ public class E4KTunerEditor extends TunerConfigurationEditor private void init() { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][grow]")); + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][][][][][][][grow]")); - add(new JLabel("E4000 Tuner Configuration"), "span,align center"); + add(new JLabel("Tuner:")); + add(getTunerIdLabel()); + add(getTunerInfoButton()); - /** - * Tuner Configuration Name - */ - add(new JLabel("Name:")); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() - { - @Override - public void focusLost(FocusEvent e) - { - save(); - } + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); - @Override - public void focusGained(FocusEvent e) - { - } - }); + add(getButtonPanel(), "span,align left"); - add(mConfigurationName, "span 2"); + add(new JSeparator(), "span,growx,push"); - mTunerInfo = new JButton("Tuner Info"); - mTunerInfo.setEnabled(false); - mTunerInfo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(mTunerInfo); + add(new JLabel("Frequency (MHz):")); + add(getFrequencyPanel(), "span 2"); - mComboSampleRate = new JComboBox<>(SampleRate.values()); - mComboSampleRate.setEnabled(false); - mComboSampleRate.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - SampleRate sampleRate = (SampleRate)mComboSampleRate.getSelectedItem(); - - try - { - mController.setSampleRate(sampleRate); - save(); - } - catch(SourceException | LibUsbException eSampleRate) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, - "E4K Tuner Controller - couldn't apply the sample rate setting [" + - sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage()); - - mLog.error("E4K Tuner Controller - couldn't apply sample rate setting [" + - sampleRate.getLabel() + "]", eSampleRate); - } - } - }); add(new JLabel("Sample Rate:")); - add(mComboSampleRate); + add(getSampleRateCombo(), "wrap"); - SpinnerModel model = new SpinnerNumberModel(0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1); //step + add(new JSeparator(), "span,growx,push"); + add(new JLabel("Mixer/LNA Gain Control"), "wrap"); - mFrequencyCorrection = new JSpinner(model); - mFrequencyCorrection.setEnabled(false); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits(1); - editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - - mFrequencyCorrection.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection(value); - save(); - } - catch(SourceException e1) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply frequency correction value: " + value + e1.getLocalizedMessage()); - - mLog.error("E4K Tuner Controller - couldn't apply frequency correction value:" - + value, e1); - } - } - }); - - add(new JLabel("PPM:")); - add(mFrequencyCorrection); - - add(new JSeparator(JSeparator.HORIZONTAL), "span,grow"); - add(new JLabel("Gain"), "wrap"); - - mComboMasterGain = new JComboBox(E4KGain.values()); - mComboMasterGain.setEnabled(false); - mComboMasterGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); - - mController.setGain((E4KGain)mComboMasterGain.getSelectedItem(), true); - - if(gain == E4KGain.MANUAL) - { - mComboMixerGain.setSelectedItem(mController.getMixerGain(true)); - mComboMixerGain.setEnabled(true); - - mComboLNAGain.setSelectedItem(mController.getLNAGain(true)); - mComboLNAGain.setEnabled(true); - - mComboEnhanceGain.setSelectedItem(mController.getEnhanceGain(true)); - mComboEnhanceGain.setEnabled(true); - } - else - { - mComboMixerGain.setEnabled(false); - mComboMixerGain.setSelectedItem(gain.getMixerGain()); - - mComboLNAGain.setEnabled(false); - mComboLNAGain.setSelectedItem(gain.getLNAGain()); - - mComboEnhanceGain.setEnabled(false); - mComboEnhanceGain.setSelectedItem(gain.getEnhanceGain()); - } - - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the gain setting - " + e.getLocalizedMessage()); - - mLog.error("E4K Tuner Controller - couldn't apply gain setting", e); - } - } - }); - - mComboMasterGain.setToolTipText("Select AUTOMATIC for auto " - + "gain, MANUAL to enable
independent control of " - + "Mixer, LNA and Enhance gain
" - + "settings, or one of the individual gain settings for
" - + "semi-manual gain control"); add(new JLabel("Master:")); - add(mComboMasterGain); + add(getMasterGainCombo(), "wrap"); - mComboMixerGain = new JComboBox(E4KMixerGain.values()); - mComboMixerGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - E4KMixerGain mixerGain = (E4KMixerGain)mComboMixerGain.getSelectedItem(); - mController.setMixerGain(mixerGain, true); - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage()); - - mLog.error("E4K Tuner Controller - couldn't apply mixer gain setting", e); - } - } - }); - mComboMixerGain.setToolTipText("Mixer Gain. Set master gain " - + "to MASTER to enable adjustment"); - mComboMixerGain.setEnabled(false); add(new JLabel("Mixer:")); - add(mComboMixerGain); + add(getMixerGainCombo(), "wrap"); - mComboLNAGain = new JComboBox(E4KLNAGain.values()); - mComboLNAGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); - mController.setLNAGain(lnaGain, true); - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage()); - - mLog.error("E4K Tuner Controller - couldn't apply LNA gain setting - ", e); - } - } - }); - mComboLNAGain.setToolTipText("LNA Gain. Set master gain " - + "to MANUAL to enable adjustment"); - mComboLNAGain.setEnabled(false); add(new JLabel("LNA:")); - add(mComboLNAGain); + add(getLNAGainCombo(), "wrap"); - mComboEnhanceGain = new JComboBox(E4KEnhanceGain.values()); - mComboEnhanceGain.addActionListener(new ActionListener() + add(new JSeparator(), "span,growx,push"); + + add(new JLabel("IF Gain:")); + add(getIfGainCombo(), "wrap"); + } + + @Override + protected void tunerStatusUpdated() + { + setLoading(true); + + if(hasTuner()) { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - E4KEnhanceGain enhance = (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); - mController.setEnhanceGain(enhance, true); - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the enhance gain setting - " + e.getLocalizedMessage()); + getTunerIdLabel().setText(getTuner().getPreferredName()); + } + else + { + getTunerIdLabel().setText(getDiscoveredTuner().getId()); + } + getTunerStatusLabel().setText(getDiscoveredTuner().getTunerStatus().toString()); + getButtonPanel().updateControls(); + getFrequencyPanel().updateControls(); - mLog.error("E4K Tuner Controller - couldn't apply enhance gain setting", e); - } + if(hasTuner()) + { + getTunerInfoButton().setEnabled(true); + getSampleRateCombo().setEnabled(true); + getSampleRateCombo().setSelectedItem(getConfiguration().getSampleRate()); + getMasterGainCombo().setEnabled(true); + getIfGainCombo().setEnabled(true); + getIfGainCombo().setSelectedItem(getConfiguration().getIFGain()); + + E4KGain gain = getConfiguration().getMasterGain(); + getMasterGainCombo().setEnabled(true); + getMasterGainCombo().setSelectedItem(gain); + + if(gain == E4KGain.MANUAL) + { + getMixerGainCombo().setSelectedItem(getConfiguration().getMixerGain()); + getMixerGainCombo().setEnabled(true); + + getLNAGainCombo().setSelectedItem(getConfiguration().getLNAGain()); + getLNAGainCombo().setEnabled(true); } - }); - mComboEnhanceGain.setToolTipText("Enhance Gain. Set master gain " - + "to MANUAL to enable adjustment"); - mComboEnhanceGain.setEnabled(false); - add(new JLabel("Enhance:")); - add(mComboEnhanceGain); + else + { + getMixerGainCombo().setEnabled(false); + getMixerGainCombo().setSelectedItem(gain.getMixerGain()); + + getLNAGainCombo().setEnabled(false); + getLNAGainCombo().setSelectedItem(gain.getLNAGain()); + } + } + else + { + getTunerInfoButton().setEnabled(false); + getSampleRateCombo().setEnabled(false); + getMasterGainCombo().setEnabled(false); + getMixerGainCombo().setEnabled(false); + getLNAGainCombo().setEnabled(false); + getIfGainCombo().setEnabled(false); + } + + updateSampleRateToolTip(); + + setLoading(false); + } + + private JComboBox getIfGainCombo() + { + if(mIfGainCombo == null) + { + mIfGainCombo = new JComboBox<>(E4KEmbeddedTuner.IFGain.values()); + mIfGainCombo.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + if(!isLoading()) + { + try + { + E4KEmbeddedTuner.IFGain selected = (E4KEmbeddedTuner.IFGain) getIfGainCombo().getSelectedItem(); + getEmbeddedTuner().setIFGain(selected, true); + save(); + } + catch(LibUsbException lue) + { + JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - " + + "couldn't apply the IF setting - " + lue.getLocalizedMessage()); + mLog.error("E4000 Tuner Controller - couldn't apply IF gain setting", e); + } + } + } + }); + mIfGainCombo.setToolTipText("Linear IF Gain"); + mIfGainCombo.setEnabled(true); + } + + return mIfGainCombo; + } + + private JComboBox getLNAGainCombo() + { + if(mLNAGainCombo == null) + { + mLNAGainCombo = new JComboBox<>(E4KLNAGain.values()); + mLNAGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + E4KLNAGain lnaGain = (E4KLNAGain) mLNAGainCombo.getSelectedItem(); + getEmbeddedTuner().setLNAGain(lnaGain, true); + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - " + + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage()); + mLog.error("E4000 Tuner Controller - couldn't apply LNA gain setting - ", e); + } + } + }); + mLNAGainCombo.setToolTipText("LNA Gain. Set master gain to MANUAL to enable adjustment"); + mLNAGainCombo.setEnabled(false); + } + + return mLNAGainCombo; + } + + private JComboBox getMixerGainCombo() + { + if(mMixerGainCombo == null) + { + mMixerGainCombo = new JComboBox<>(E4KMixerGain.values()); + mMixerGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + E4KMixerGain mixerGain = (E4KMixerGain) mMixerGainCombo.getSelectedItem(); + getEmbeddedTuner().setMixerGain(mixerGain, true); + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - " + + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage()); + mLog.error("E4000 Tuner Controller - couldn't apply mixer gain setting", e); + } + } + }); + mMixerGainCombo.setToolTipText("Mixer Gain. Set master gain to MASTER to enable adjustment"); + mMixerGainCombo.setEnabled(false); + } + + return mMixerGainCombo; + } + + private JComboBox getMasterGainCombo() + { + if(mMasterGainCombo == null) + { + mMasterGainCombo = new JComboBox<>(E4KGain.values()); + mMasterGainCombo.setEnabled(false); + mMasterGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + E4KGain gain = (E4KGain) mMasterGainCombo.getSelectedItem(); + getEmbeddedTuner().setGain((E4KGain) mMasterGainCombo.getSelectedItem(), true); + if(gain == E4KGain.MANUAL) + { + getMixerGainCombo().setSelectedItem(getEmbeddedTuner().getMixerGain(true)); + getMixerGainCombo().setEnabled(true); + getLNAGainCombo().setSelectedItem(getEmbeddedTuner().getLNAGain(true)); + getLNAGainCombo().setEnabled(true); + } + else + { + getMixerGainCombo().setEnabled(false); + getMixerGainCombo().setSelectedItem(gain.getMixerGain()); + getLNAGainCombo().setEnabled(false); + getLNAGainCombo().setSelectedItem(gain.getLNAGain()); + } + + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - " + + "couldn't apply the gain setting - " + e.getLocalizedMessage()); + mLog.error("E4000 Tuner Controller - couldn't apply gain setting", e); + } + } + }); + + mMasterGainCombo.setToolTipText("Select AUTOMATIC for auto gain, MANUAL to enable
" + + "independent control of Mixer, LNA and Enhance gain
settings, or one of the " + + "individual gain settings for
semi-manual gain control"); + } + + return mMasterGainCombo; + } + + private JComboBox getSampleRateCombo() + { + if(mSampleRateCombo == null) + { + mSampleRateCombo = new JComboBox<>(SampleRate.values()); + mSampleRateCombo.setEnabled(false); + mSampleRateCombo.addActionListener(e -> + { + if(!isLoading()) + { + SampleRate sampleRate = (SampleRate) mSampleRateCombo.getSelectedItem(); + + try + { + getTuner().getController().setSampleRate(sampleRate); + save(); + } + catch(SourceException | LibUsbException eSampleRate) + { + JOptionPane.showMessageDialog(E4KTunerEditor.this, + "E4000 Tuner Controller - couldn't apply the sample rate setting [" + + sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage()); + + mLog.error("E4000 Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", eSampleRate); + } + } + }); + } + + return mSampleRateCombo; + } + + private JButton getTunerInfoButton() + { + if(mTunerInfoButton == null) + { + mTunerInfoButton = new JButton("Tuner Info"); + mTunerInfoButton.setEnabled(false); + mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(E4KTunerEditor.this, + getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE)); + } + + return mTunerInfoButton; } + /** + * Updates the sample rate tooltip according to the tuner controller's lock state. + */ /** * Updates the sample rate tooltip according to the tuner controller's lock state. */ private void updateSampleRateToolTip() { - if(mController.isLocked()) + if(hasTuner() && getTuner().getTunerController().isLocked()) { - mComboSampleRate.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + } + else if(hasTuner()) + { + getSampleRateCombo().setToolTipText("Select a sample rate for the tuner"); } else { - mComboSampleRate.setToolTipText("Select a sample rate for the tuner"); + getSampleRateCombo().setToolTipText("No tuner available"); } } @Override public void setTunerLockState(boolean locked) { - mComboSampleRate.setEnabled(!locked); - } - - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled(boolean enabled) - { - if(mConfigurationName.isEnabled() != enabled) - { - mConfigurationName.setEnabled(enabled); - } - - if(mTunerInfo.isEnabled() != enabled) - { - mTunerInfo.setEnabled(enabled); - } - - if(mFrequencyCorrection.isEnabled() != enabled) - { - mFrequencyCorrection.setEnabled(enabled); - } - - updateSampleRateToolTip(); - - if(mController.isLocked()) - { - mComboSampleRate.setEnabled(false); - } - else if(mComboSampleRate.isEnabled() != enabled) - { - mComboSampleRate.setEnabled(enabled); - } - - if(mComboMasterGain.isEnabled() != enabled) - { - mComboMasterGain.setEnabled(enabled); - } - - if(mComboMixerGain.isEnabled() != enabled) - { - mComboMixerGain.setEnabled(enabled); - } - - if(mComboLNAGain.isEnabled() != enabled) - { - mComboLNAGain.setEnabled(enabled); - } - - if(mComboEnhanceGain.isEnabled() != enabled) - { - mComboEnhanceGain.setEnabled(enabled); - } + super.setTunerLockState(locked); + mSampleRateCombo.setEnabled(!locked); } private String getTunerInfo() { StringBuilder sb = new StringBuilder(); - RTL2832TunerController.Descriptor descriptor = mController.getDescriptor(); + RTL2832TunerController.Descriptor descriptor = getTuner().getController().getDescriptor(); sb.append("

RTL-2832 with E4000 Tuner

"); @@ -461,68 +451,20 @@ public class E4KTunerEditor extends TunerConfigurationEditor @Override public void save() { - if(hasItem() && !mLoading) + if(hasConfiguration() && !isLoading()) { E4KTunerConfiguration config = getConfiguration(); - - config.setName(mConfigurationName.getText()); - - config.setSampleRate((SampleRate)mComboSampleRate.getSelectedItem()); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); + config.setFrequency(getFrequencyControl().getFrequency()); + double value = ((SpinnerNumberModel)getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); config.setFrequencyCorrection(value); + config.setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected()); - config.setMasterGain((E4KGain)mComboMasterGain.getSelectedItem()); - config.setMixerGain((E4KMixerGain)mComboMixerGain.getSelectedItem()); - config.setLNAGain((E4KLNAGain)mComboLNAGain.getSelectedItem()); - config.setEnhanceGain((E4KEnhanceGain)mComboEnhanceGain.getSelectedItem()); - - getTunerConfigurationModel().broadcast(new TunerConfigurationEvent( - getConfiguration(), TunerConfigurationEvent.Event.CHANGE)); + config.setSampleRate((SampleRate)getSampleRateCombo().getSelectedItem()); + config.setMasterGain((E4KGain)getMasterGainCombo().getSelectedItem()); + config.setMixerGain((E4KMixerGain)getMixerGainCombo().getSelectedItem()); + config.setLNAGain((E4KLNAGain)getLNAGainCombo().getSelectedItem()); + config.setIFGain((E4KEmbeddedTuner.IFGain)getIfGainCombo().getSelectedItem()); + saveConfiguration(); } - - } - - @Override - public void setItem(TunerConfiguration tunerConfiguration) - { - super.setItem(tunerConfiguration); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if(hasItem()) - { - E4KTunerConfiguration config = getConfiguration(); - - if(tunerConfiguration.isAssigned()) - { - setControlsEnabled(true); - - mConfigurationName.setText(config.getName()); - mFrequencyCorrection.setValue(config.getFrequencyCorrection()); - mComboSampleRate.setSelectedItem(config.getSampleRate()); - mComboMasterGain.setSelectedItem(config.getMasterGain()); - mComboMixerGain.setSelectedItem(config.getMixerGain()); - mComboLNAGain.setSelectedItem(config.getLNAGain()); - mComboEnhanceGain.setSelectedItem(config.getEnhanceGain()); - - //Update enabled state to reflect when frequency and sample rate controls are locked - mComboSampleRate.setEnabled(!mController.isLocked()); - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(config.getName()); - } - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(""); - } - - mLoading = false; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TEmbeddedTuner.java similarity index 73% rename from src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java rename to src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TEmbeddedTuner.java index 751a923a..f43b86c1 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TEmbeddedTuner.java @@ -1,105 +1,66 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * Java port of librtlsdr + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Copyright (C) 2013 Mauro Carvalho Chehab - * Copyright (C) 2013 Steve Markgraf + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.rtl.r820t; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.FrequencyErrorCorrectionManager; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import io.github.dsheirer.source.tuner.rtl.EmbeddedTuner; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.LibUsb; import org.usb4java.LibUsbException; import javax.usb.UsbException; import java.nio.ByteBuffer; -public class R820TTunerController extends RTL2832TunerController +public class R820TEmbeddedTuner extends EmbeddedTuner { - private final static Logger mLog = LoggerFactory.getLogger(R820TTunerController.class); - - public static final long MIN_FREQUENCY = 3180000; - public static final long MAX_FREQUENCY = 1782030000; - public static final double USABLE_BANDWIDTH_PERCENT = 0.98; - public static final int DC_SPIKE_AVOID_BUFFER = 5000; - - public static final int R820T_IF_FREQUENCY = 3570000; - public static final byte VERSION = (byte) 49; - - public static final byte[] BIT_REV_LOOKUP_TABLE = - {(byte) 0x0, (byte) 0x8, (byte) 0x4, (byte) 0xC, - (byte) 0x2, (byte) 0xA, (byte) 0x6, (byte) 0xE, - (byte) 0x1, (byte) 0x9, (byte) 0x5, (byte) 0xD, - (byte) 0x3, (byte) 0xB, (byte) 0x7, (byte) 0xF}; - - /* R820T I2C address */ - private byte mI2CAddress = (byte) 0x34; - - /* Shadow register is used to keep a cached (in-memory) copy of all - * registers, so that we don't have to read a full byte from a register in - * order to apply a masked value and then re-write the full byte. With the - * shadow register, we can apply the masked value to the cached value, and - * then just write the masked byte, skipping the need to read the byte - * first. */ - private int[] mShadowRegister = - {0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x32, 0x75, - 0xC0, 0x40, 0xD6, 0x6C, 0xF5, 0x63, 0x75, 0x68, - 0x6C, 0x83, 0x80, 0x00, 0x0F, 0x00, 0xC0, 0x30, - 0x48, 0xCC, 0x60, 0x00, 0x54, 0xAE, 0x4A, 0xC0}; - - public FrequencyErrorCorrectionManager mFrequencyErrorCorrectionManager; - - public R820TTunerController(Device device, DeviceDescriptor deviceDescriptor) throws SourceException - { - super(device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER, - USABLE_BANDWIDTH_PERCENT); - - mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this); - } + private final static Logger mLog = LoggerFactory.getLogger(R820TEmbeddedTuner.class); + private static final long MINIMUM_SUPPORTED_FREQUENCY = 3180000; + private static final long MAXIMUM_SUPPORTED_FREQUENCY = 1782030000; + private static final double USABLE_BANDWIDTH_PERCENT = 0.98; + private static final int DC_SPIKE_AVOID_BUFFER = 5000; + private static final int R820T_IF_FREQUENCY = 3570000; + private static final byte VERSION = (byte) 49; + private static final byte I2C_ADDRESS = (byte) 0x34; + public static final byte[] BIT_REV_LOOKUP_TABLE = {(byte) 0x0, (byte) 0x8, (byte) 0x4, (byte) 0xC, (byte) 0x2, + (byte) 0xA, (byte) 0x6, (byte) 0xE, (byte) 0x1, (byte) 0x9, (byte) 0x5, (byte) 0xD, (byte) 0x3, (byte) 0xB, + (byte) 0x7, (byte) 0xF}; /** - * Manager for applying automatic frequency error PPM adjustments to the tuner controller based on - * frequency error measurements received from certain downstream decoders (e.g. P25). - * @return manager + * Shadow register is used to keep a cached (in-memory) copy of all registers, so that we don't have to read a + * full byte from a register in order to apply a masked value and then re-write the full byte. With the shadow + * register, we can apply the masked value to the cached value, and then just write the masked byte, skipping the + * need to read the byte first. */ - public FrequencyErrorCorrectionManager getFrequencyErrorCorrectionManager() - { - return mFrequencyErrorCorrectionManager; - } + private int[] mShadowRegister = {0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x32, 0x75, 0xC0, 0x40, 0xD6, 0x6C, 0xF5, 0x63, + 0x75, 0x68, 0x6C, 0x83, 0x80, 0x00, 0x0F, 0x00, 0xC0, 0x30, 0x48, 0xCC, 0x60, 0x00, 0x54, 0xAE, 0x4A, 0xC0}; + /** - * Overrides updates for measured frequency error so that the updates can also be applied to the - * frequency error correction manager for automatic PPM updating. - * @param measuredFrequencyError in hertz averaged over a 5 second interval. + * Constructs an instance + * @param adapter for accessing RTL2832USBController interfaces */ - @Override - public void setMeasuredFrequencyError(int measuredFrequencyError) + public R820TEmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter) { - super.setMeasuredFrequencyError(measuredFrequencyError); - getFrequencyErrorCorrectionManager().updatePPM(getPPMFrequencyError()); + super(adapter); } @Override @@ -109,102 +70,92 @@ public class R820TTunerController extends RTL2832TunerController } @Override - public void setSampleRateFilters(int sampleRate) throws LibUsbException + public long getMinimumFrequencySupported() { - //TODO: why is this being forced as an abstract method on sub classes? + return MINIMUM_SUPPORTED_FREQUENCY; } + @Override + public long getMaximumFrequencySupported() + { + return MAXIMUM_SUPPORTED_FREQUENCY; + } + + @Override + public int getDcSpikeHalfBandwidth() + { + return DC_SPIKE_AVOID_BUFFER; + } + + @Override + public double getUsableBandwidthPercent() + { + return USABLE_BANDWIDTH_PERCENT; + } + + @Override + public void setSampleRateFilters(int sampleRate) throws SourceException + { + //No-op + } + + /** + * Applies the tuner configuration values to this embedded tuner + * @param tunerConfig containing settings to apply + * @throws SourceException if there is an error + */ @Override public void apply(TunerConfiguration tunerConfig) throws SourceException { - if(tunerConfig != null && - tunerConfig instanceof R820TTunerConfiguration) - { - R820TTunerConfiguration config = (R820TTunerConfiguration) tunerConfig; + //Invoke super for frequency, frequency correction and autoPPM + if(tunerConfig instanceof R820TTunerConfiguration config) + { try { - SampleRate sampleRate = config.getSampleRate(); - setSampleRate(sampleRate); - - double correction = config.getFrequencyCorrection(); - setFrequencyCorrection(correction); - - getFrequencyErrorCorrectionManager().setEnabled(config.getAutoPPMCorrectionEnabled()); - R820TGain masterGain = config.getMasterGain(); setGain(masterGain, true); if(masterGain == R820TGain.MANUAL) { - R820TMixerGain mixerGain = config.getMixerGain(); - setMixerGain(mixerGain, true); - - R820TLNAGain lnaGain = config.getLNAGain(); - setLNAGain(lnaGain, true); - - R820TVGAGain vgaGain = config.getVGAGain(); - setVGAGain(vgaGain, true); - } - - try - { - setFrequency(config.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency + setLNAGain(config.getLNAGain(), true); + setMixerGain(config.getMixerGain(), true); + setVGAGain(config.getVGAGain(), true); } } catch(UsbException e) { - throw new SourceException("R820TTunerController - usb error " - + "while applying tuner config", e); + throw new SourceException("R820TTunerController - usb error while applying tuner config", e); } } } - /** - * Not implemented. - */ - @Override - public long getTunedFrequency() throws SourceException - { - return 0; - } - /** * Sets the center frequency. Setting the frequency is a two-part process * of setting the multiplexer and then setting the Oscillator (PLL). */ @Override - public void setTunedFrequency(long frequency) throws SourceException + public synchronized void setTunedFrequency(long frequency) throws SourceException { - mLock.lock(); + getAdapter().getLock().lock(); try { - enableI2CRepeater(mDeviceHandle, true); - + getAdapter().enableI2CRepeater(); boolean controlI2C = false; - long offsetFrequency = frequency + R820T_IF_FREQUENCY; - setMux(offsetFrequency, controlI2C); - setPLL(offsetFrequency, controlI2C); - - enableI2CRepeater(mDeviceHandle, false); + getAdapter().disableI2CRepeater(); } catch(UsbException e) { - throw new SourceException("R820TTunerController - exception " - + "while setting frequency [" + frequency + "] - " + - e.getLocalizedMessage()); + throw new SourceException("R820TTunerController - exception while setting frequency [" + frequency + "] - " + + e.getLocalizedMessage()); } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } @@ -212,31 +163,18 @@ public class R820TTunerController extends RTL2832TunerController * Overrides the same method from the RTL2832 tuner controller to apply * settings specific to the R820T tuner. */ - public void setSamplingMode(SampleMode mode) throws LibUsbException + public void setSamplingMode(RTL2832TunerController.SampleMode mode) throws LibUsbException { - switch(mode) + if(mode == RTL2832TunerController.SampleMode.QUADRATURE) { - case QUADRATURE: - /* Set intermediate frequency to R820T IF frequency */ - setIFFrequency(R820T_IF_FREQUENCY); + /* Set intermediate frequency to R820T IF frequency */ + getAdapter().setIFFrequency(R820T_IF_FREQUENCY); - /* Enable spectrum inversion */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x15, - (short) 0x01, - 1); - - /* Set default i/q path */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x06, - (short) 0x80, - 1); - break; - default: - break; + /* Enable spectrum inversion */ + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ONE, (short) 0x15, (short) 0x01, 1); + /* Set default i/q path */ + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ZERO, (short) 0x06, (short) 0x80, 1); } } @@ -257,8 +195,7 @@ public class R820TTunerController extends RTL2832TunerController writeR820TRegister(Register.TF_BAND, range.getTFC(), controlI2C); /* XTAL CAP & Drive */ - writeR820TRegister(Register.PLL_XTAL_CAPACITOR_AND_DRIVE, - range.getXTALHighCap0P(), controlI2C); + writeR820TRegister(Register.PLL_XTAL_CAPACITOR_AND_DRIVE, range.getXTALHighCap0P(), controlI2C); /* Register 8 - what is it? */ writeR820TRegister(Register.UNKNOWN_REGISTER_8, (byte) 0x00, controlI2C); @@ -267,86 +204,25 @@ public class R820TTunerController extends RTL2832TunerController writeR820TRegister(Register.UNKNOWN_REGISTER_9, (byte) 0x00, controlI2C); } - /** - * Initializes the tuner for use. - */ - public void init() throws SourceException - { - /* Initialize the super class to open and claim the usb interface*/ - super.init(); - - try - { - initBaseband(mDeviceHandle); - - enableI2CRepeater(mDeviceHandle, true); - - boolean i2CRepeaterControl = false; - - initTuner(i2CRepeaterControl); - - enableI2CRepeater(mDeviceHandle, false); - } - catch(UsbException e) - { - throw new SourceException("error during init()", e); - } - } - - @Override - public void dispose() - { - mLog.info("Releasing R820T Tuner"); - - if(mDeviceHandle != null) - { - try - { - LibUsb.close(mDeviceHandle); - } - catch(Exception e) - { - mLog.error("error while closing device handle", e); - } - - mDeviceHandle = null; - } - } - /** * Initializes the tuner section. */ - public void initTuner(boolean controlI2C) throws UsbException + protected void initTuner() throws UsbException { /* Disable zero IF mode */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0xB1, - (short) 0x1A, - 1); + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ONE, (short) 0xB1, (short) 0x1A, 1); /* Only enable in-phase ADC input */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x08, - (short) 0x4D, - 1); + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ZERO, (short) 0x08, (short) 0x4D, 1); /* Set intermediate frequency to R820T IF frequency (3.57 MHz) */ - setIFFrequency(R820T_IF_FREQUENCY); + getAdapter().setIFFrequency(R820T_IF_FREQUENCY); /* Enable spectrum inversion */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x15, - (short) 0x01, - 1); - - initializeRegisters(controlI2C); - - setTVStandard(controlI2C); - - systemFrequencySelect(0, controlI2C); + getAdapter().writeDemodRegister(RTL2832TunerController.Page.ONE, (short) 0x15, (short) 0x01, 1); + initializeRegisters(false); + setTVStandard(false); + systemFrequencySelect(0, false); } /** @@ -392,8 +268,7 @@ public class R820TTunerController extends RTL2832TunerController if(!calibrationSuccessful(calibrationCode)) { - mLog.error("Calibration NOT successful - code: " + - calibrationCode); + mLog.error("Calibration NOT successful - code: " + calibrationCode); } } @@ -453,8 +328,7 @@ public class R820TTunerController extends RTL2832TunerController /** * Sets the system IF frequency */ - private void systemFrequencySelect(long frequency, boolean controlI2C) - throws UsbException + private void systemFrequencySelect(long frequency, boolean controlI2C) throws UsbException { /* LNA top? */ writeR820TRegister(Register.LNA_TOP2, (byte) 0xE5, controlI2C); @@ -463,9 +337,7 @@ public class R820TTunerController extends RTL2832TunerController byte cp_cur; byte div_buf_cur; - if(frequency == 506000000 || - frequency == 666000000 || - frequency == 818000000) + if(frequency == 506000000 || frequency == 666000000 || frequency == 818000000) { mixer_top = (byte) 0x14; cp_cur = (byte) 0x28; @@ -479,46 +351,29 @@ public class R820TTunerController extends RTL2832TunerController } writeR820TRegister(Register.MIXER_TOP, mixer_top, controlI2C); - writeR820TRegister(Register.LNA_VTH_L, (byte) 0x53, controlI2C); - writeR820TRegister(Register.MIXER_VTH_L, (byte) 0x75, controlI2C); - /* Air-In only for Astrometa */ writeR820TRegister(Register.AIR_CABLE1_INPUT_SELECTOR, (byte) 0x00, controlI2C); - writeR820TRegister(Register.CABLE2_INPUT_SELECTOR, (byte) 0x00, controlI2C); - writeR820TRegister(Register.CP_CUR, cp_cur, controlI2C); - writeR820TRegister(Register.DIVIDER_BUFFER_CURRENT, div_buf_cur, controlI2C); - writeR820TRegister(Register.FILTER_CURRENT, (byte) 0x40, controlI2C); - /* if( type != TUNER_ANALOG_TV ) ... */ - writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C); - writeR820TRegister(Register.MIXER_TOP2, (byte) 0x00, controlI2C); - writeR820TRegister(Register.PRE_DETECT, (byte) 0x00, controlI2C); - writeR820TRegister(Register.AGC_CLOCK, (byte) 0x30, controlI2C); - writeR820TRegister(Register.LNA_TOP, (byte) 0x18, controlI2C); - writeR820TRegister(Register.MIXER_TOP2, mixer_top, controlI2C); - /* LNA discharge current */ writeR820TRegister(Register.LNA_DISCHARGE_CURRENT, (byte) 0x14, controlI2C); - /* AGC clock 1 khz, external det1 cap 1u */ writeR820TRegister(Register.AGC_CLOCK, (byte) 0x20, controlI2C); } /** - * Sets the tuner's Phase-Locked-Loop (PLL) oscillator used for frequency - * (tuning) control + * Sets the tuner's Phase-Locked-Loop (PLL) oscillator used for frequency (tuning) control * * @param frequency - desired center frequency * @param controlI2C - control the I2C repeater locally @@ -528,49 +383,32 @@ public class R820TTunerController extends RTL2832TunerController { /* Set reference divider to 0 */ writeR820TRegister(Register.REFERENCE_DIVIDER_2, (byte) 0x00, controlI2C); - /* Set PLL autotune to 128kHz */ writeR820TRegister(Register.PLL_AUTOTUNE, (byte) 0x00, controlI2C); - /* Set VCO current to 100 */ writeR820TRegister(Register.VCO_CURRENT, (byte) 0x80, controlI2C); - /* Set the frequency divider - adjust for vco_fine_tune status */ FrequencyDivider divider = FrequencyDivider.fromFrequency(frequency); - int statusRegister4 = getStatusRegister(4, controlI2C); - int vco_fine_tune = (statusRegister4 & 0x30) >> 4; - int div_num = divider.getDividerNumber(vco_fine_tune); - writeR820TRegister(Register.DIVIDER, (byte) (div_num << 5), controlI2C); - /* Get the integral number for this divider and frequency */ Integral integral = divider.getIntegral(frequency); - writeR820TRegister(Register.PLL, integral.getRegisterValue(), controlI2C); - /* Calculate the sigma-delta modulator fractional setting. If it's * non-zero, power up the sdm and apply the fractional setting, * otherwise turn it off */ int sdm = divider.getSDM(integral, frequency); - if(sdm != 0) { - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER, - (byte) 0x00, controlI2C); - - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_MSB, - (byte) ((sdm >> 8) & 0xFF), controlI2C); - - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_LSB, - (byte) (sdm & 0xFF), controlI2C); + writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER, (byte) 0x00, controlI2C); + writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_MSB, (byte) ((sdm >> 8) & 0xFF), controlI2C); + writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_LSB, (byte) (sdm & 0xFF), controlI2C); } else { - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER, - (byte) 0x08, controlI2C); + writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER, (byte) 0x08, controlI2C); } /* Check to see if the PLL locked with these divider, integral and sdm @@ -582,8 +420,7 @@ public class R820TTunerController extends RTL2832TunerController if(!isPLLLocked(controlI2C)) { - throw new UsbException("R820T Tuner Controller - couldn't " - + "achieve PLL lock on frequency [" + frequency + "]"); + throw new UsbException("R820T Tuner Controller - couldn't achieve PLL lock on frequency [" + frequency + "]"); } } @@ -599,7 +436,6 @@ public class R820TTunerController extends RTL2832TunerController private boolean isPLLLocked(boolean controlI2C) throws UsbException { int register = getStatusRegister(2, controlI2C); - return (register & 0x40) == 0x40; } @@ -614,11 +450,7 @@ public class R820TTunerController extends RTL2832TunerController { for(int x = 5; x < mShadowRegister.length; x++) { - writeI2CRegister(mDeviceHandle, - mI2CAddress, - (byte) x, - (byte) mShadowRegister[x], - controlI2C); + getAdapter().writeI2CRegister(I2C_ADDRESS, (byte) x, (byte) mShadowRegister[x], controlI2C); } } @@ -628,9 +460,7 @@ public class R820TTunerController extends RTL2832TunerController private int getStatusRegister(int register, boolean controlI2C) throws UsbException { ByteBuffer buffer = ByteBuffer.allocateDirect(5); - - read(mDeviceHandle, mI2CAddress, Block.I2C, buffer); - + getAdapter().read(I2C_ADDRESS, RTL2832TunerController.Block.I2C, buffer); return bitReverse(buffer.get(register) & 0xFF); } @@ -639,48 +469,31 @@ public class R820TTunerController extends RTL2832TunerController */ private static int bitReverse(int value) { - return BIT_REV_LOOKUP_TABLE[value & 0x0F] << 4 | - BIT_REV_LOOKUP_TABLE[(value & 0xF0) >> 4]; + return BIT_REV_LOOKUP_TABLE[value & 0x0F] << 4 | BIT_REV_LOOKUP_TABLE[(value & 0xF0) >> 4]; } /** * Writes the byte value to the specified register, optionally controlling * the I2C repeater as needed. */ - public void writeR820TRegister(Register register, - byte value, - boolean controlI2C) throws UsbException + public synchronized void writeR820TRegister(Register register, byte value, boolean controlI2C) throws UsbException { if(register.isMasked()) { int current = mShadowRegister[register.getRegister()]; - - value = (byte) ((current & ~register.getMask()) | - (value & register.getMask())); + value = (byte) ((current & ~register.getMask()) | (value & register.getMask())); } - writeI2CRegister(mDeviceHandle, mI2CAddress, - (byte) register.getRegister(), value, controlI2C); - + getAdapter().writeI2CRegister(I2C_ADDRESS, (byte) register.getRegister(), value, controlI2C); mShadowRegister[register.getRegister()] = value; - -// Log.info( "R820T writing register " + -// String.format( "%02X", register.getRegister() ) + " value " + -// String.format( "%02X", value ) ); } /** * Reads the specified register, optionally controlling the I2C repeater */ - public int readR820TRegister(Register register, boolean controlI2C) - throws UsbException + public int readR820TRegister(Register register, boolean controlI2C) throws UsbException { - int value = readI2CRegister(mDeviceHandle, - mI2CAddress, - (byte) register.getRegister(), - controlI2C); - - return value; + return getAdapter().readI2CRegister(I2C_ADDRESS, (byte) register.getRegister(), controlI2C); } /** @@ -699,7 +512,7 @@ public class R820TTunerController extends RTL2832TunerController */ public void setLNAGain(R820TLNAGain gain, boolean controlI2C) throws UsbException { - mLock.lock(); + getAdapter().getLock().lock(); try { @@ -707,7 +520,7 @@ public class R820TTunerController extends RTL2832TunerController } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } @@ -716,7 +529,7 @@ public class R820TTunerController extends RTL2832TunerController */ public void setMixerGain(R820TMixerGain gain, boolean controlI2C) throws UsbException { - mLock.lock(); + getAdapter().getLock().lock(); try { @@ -724,7 +537,7 @@ public class R820TTunerController extends RTL2832TunerController } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } @@ -733,7 +546,7 @@ public class R820TTunerController extends RTL2832TunerController */ public void setVGAGain(R820TVGAGain gain, boolean controlI2C) throws UsbException { - mLock.lock(); + getAdapter().getLock().lock(); try { @@ -741,7 +554,7 @@ public class R820TTunerController extends RTL2832TunerController } finally { - mLock.unlock(); + getAdapter().getLock().unlock(); } } @@ -770,7 +583,7 @@ public class R820TTunerController extends RTL2832TunerController private String mLabel; private int mSetting; - private R820TVGAGain(String label, int setting) + R820TVGAGain(String label, int setting) { mLabel = label; mSetting = setting; @@ -813,7 +626,7 @@ public class R820TTunerController extends RTL2832TunerController private String mLabel; private int mSetting; - private R820TLNAGain(String label, int setting) + R820TLNAGain(String label, int setting) { mLabel = label; mSetting = setting; @@ -859,7 +672,7 @@ public class R820TTunerController extends RTL2832TunerController private String mLabel; private int mSetting; - private R820TMixerGain(String label, int setting) + R820TMixerGain(String label, int setting) { mLabel = label; mSetting = setting; @@ -919,7 +732,7 @@ public class R820TTunerController extends RTL2832TunerController private R820TLNAGain mLNAGain; private R820TMixerGain mMixerGain; - private R820TGain(String label, R820TVGAGain vga, R820TLNAGain lna, R820TMixerGain mixer) + R820TGain(String label, R820TVGAGain vga, R820TLNAGain lna, R820TMixerGain mixer) { mLabel = label; mVGAGain = vga; @@ -1006,7 +819,7 @@ public class R820TTunerController extends RTL2832TunerController private int mRegister; private int mMask; - private Register(int register, int mask) + Register(int register, int mask) { mRegister = register; mMask = mask; @@ -1061,13 +874,8 @@ public class R820TTunerController extends RTL2832TunerController private int mXtalCap20p; private int mXtalCap10p; - private FrequencyRange(long minFrequency, - long maxFrequency, - int openDrain, - int rfMuxPloy, - int tf_c, - int xtalCap20p, - int xtalCap10p) + FrequencyRange(long minFrequency, long maxFrequency, int openDrain, int rfMuxPloy, int tf_c, int xtalCap20p, + int xtalCap10p) { mMinFrequency = minFrequency; mMaxFrequency = maxFrequency; @@ -1078,11 +886,19 @@ public class R820TTunerController extends RTL2832TunerController mXtalCap10p = xtalCap10p; } + /** + * Indicates if the frequency is contained by the frequency range of this band + */ public boolean contains(long frequency) { return mMinFrequency <= frequency && frequency <= mMaxFrequency; } + /** + * Finds the correct frequency range that contains the frequency + * @param frequency to lookup + * @return frequency range + */ public static FrequencyRange getRangeForFrequency(long frequency) { for(FrequencyRange range : values()) @@ -1175,12 +991,8 @@ public class R820TTunerController extends RTL2832TunerController private int mIntegralValue; private static final int mVCOPowerReference = 2; - private FrequencyDivider(int dividerNumber, - int mixerDivider, - long minimumFrequency, - long maximumFrequency, - int registerSetting, - int integralValue) + FrequencyDivider(int dividerNumber, int mixerDivider, long minimumFrequency, long maximumFrequency, + int registerSetting, int integralValue) { mDividerNumber = dividerNumber; mMixerDivider = mixerDivider; @@ -1230,8 +1042,7 @@ public class R820TTunerController extends RTL2832TunerController public boolean contains(long frequency) { - return mMinimumFrequency <= frequency && - frequency <= mMaximumFrequency; + return mMinimumFrequency <= frequency && frequency <= mMaximumFrequency; } /** @@ -1269,27 +1080,22 @@ public class R820TTunerController extends RTL2832TunerController return Integral.fromValue(integral); } - throw new IllegalArgumentException("PLL frequency [" + frequency + - "] is not valid for this frequency divider " + this.toString()); + throw new IllegalArgumentException("PLL frequency [" + frequency + "] is not valid for this frequency " + + "divider " + this); } /** - * Calculates the 16-bit value of the sigma-delta modulator setting - * which represents the fractional portion of the requested frequency - * that is left over after subtracting the divider minimum frequency - * and the integral frequency units. That residual value is divided - * by the integral unit value to derive a 16-bit fractional value, - * returned as an integer + * Calculates the 16-bit value of the sigma-delta modulator setting which represents the fractional portion of + * the requested frequency that is left over after subtracting the divider minimum frequency and the integral + * frequency units. That residual value is divided by the integral unit value to derive a 16-bit fractional + * value, returned as an integer */ public int getSDM(Integral integral, long frequency) { if(contains(frequency)) { - int delta = (int) (frequency - mMinimumFrequency - - (integral.getNumber() * mIntegralValue)); - + int delta = (int) (frequency - mMinimumFrequency - (integral.getNumber() * mIntegralValue)); double fractional = (double) delta / (double) mIntegralValue; - //Left shift the double value 16 bits and truncate to an integer return (int) (fractional * 0x10000) & 0xFFFF; } @@ -1348,7 +1154,7 @@ public class R820TTunerController extends RTL2832TunerController private int mNumber; private int mRegister; - private Integral(int number, int register) + Integral(int number, int register) { mNumber = number; mRegister = register; @@ -1371,8 +1177,7 @@ public class R820TTunerController extends RTL2832TunerController return Integral.values()[value]; } - throw new IllegalArgumentException("PLL integral value [" + value + - "] must be in the range 0 - 31"); + throw new IllegalArgumentException("PLL integral value [" + value + "] must be in the range 0 - 31"); } } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerConfiguration.java index 1c1ad8d4..cdeb67b7 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerConfiguration.java @@ -1,36 +1,37 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.rtl.r820t; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; +import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration; -public class R820TTunerConfiguration extends TunerConfiguration +/** + * RTL-2832 with embedded R820T tuner configuration + */ +public class R820TTunerConfiguration extends RTL2832TunerConfiguration { - private R820TTunerController.R820TGain mMasterGain = R820TTunerController.R820TGain.GAIN_327; - private R820TTunerController.R820TMixerGain mMixerGain = R820TTunerController.R820TMixerGain.GAIN_105; - private R820TTunerController.R820TLNAGain mLNAGain = R820TTunerController.R820TLNAGain.GAIN_222; - private R820TTunerController.R820TVGAGain mVGAGain = R820TTunerController.R820TVGAGain.GAIN_210; - private double mFrequencyCorrection = 0.0d; - private RTL2832TunerController.SampleRate mSampleRate = RTL2832TunerController.SampleRate.RATE_2_400MHZ; - private boolean mAutoPPMCorrection = true; + private R820TEmbeddedTuner.R820TGain mMasterGain = R820TEmbeddedTuner.R820TGain.GAIN_327; + private R820TEmbeddedTuner.R820TMixerGain mMixerGain = R820TEmbeddedTuner.R820TMixerGain.GAIN_105; + private R820TEmbeddedTuner.R820TLNAGain mLNAGain = R820TEmbeddedTuner.R820TLNAGain.GAIN_222; + private R820TEmbeddedTuner.R820TVGAGain mVGAGain = R820TEmbeddedTuner.R820TVGAGain.GAIN_210; /** * Default constructor for JAXB @@ -39,100 +40,64 @@ public class R820TTunerConfiguration extends TunerConfiguration { } - public R820TTunerConfiguration(String uniqueID, String name) - { - super(uniqueID, name); - } - - @JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") + @JsonIgnore @Override public TunerType getTunerType() { return TunerType.RAFAELMICRO_R820T; } + /** + * Constructs an instance + * + * @param uniqueID for the tuner + */ + public R820TTunerConfiguration(String uniqueID) + { + super(uniqueID); + } + @JacksonXmlProperty(isAttribute = true, localName = "master_gain") - public R820TTunerController.R820TGain getMasterGain() + public R820TEmbeddedTuner.R820TGain getMasterGain() { return mMasterGain; } - public void setMasterGain(R820TTunerController.R820TGain gain) + public void setMasterGain(R820TEmbeddedTuner.R820TGain gain) { mMasterGain = gain; } @JacksonXmlProperty(isAttribute = true, localName = "mixer_gain") - public R820TTunerController.R820TMixerGain getMixerGain() + public R820TEmbeddedTuner.R820TMixerGain getMixerGain() { return mMixerGain; } - public void setMixerGain(R820TTunerController.R820TMixerGain mixerGain) + public void setMixerGain(R820TEmbeddedTuner.R820TMixerGain mixerGain) { mMixerGain = mixerGain; } @JacksonXmlProperty(isAttribute = true, localName = "lna_gain") - public R820TTunerController.R820TLNAGain getLNAGain() + public R820TEmbeddedTuner.R820TLNAGain getLNAGain() { return mLNAGain; } - public void setLNAGain(R820TTunerController.R820TLNAGain lnaGain) + public void setLNAGain(R820TEmbeddedTuner.R820TLNAGain lnaGain) { mLNAGain = lnaGain; } @JacksonXmlProperty(isAttribute = true, localName = "vga_gain") - public R820TTunerController.R820TVGAGain getVGAGain() + public R820TEmbeddedTuner.R820TVGAGain getVGAGain() { return mVGAGain; } - public void setVGAGain(R820TTunerController.R820TVGAGain vgaGain) + public void setVGAGain(R820TEmbeddedTuner.R820TVGAGain vgaGain) { mVGAGain = vgaGain; } - - @JacksonXmlProperty(isAttribute = true, localName = "frequency_correction") - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection(double value) - { - mFrequencyCorrection = value; - } - - @JacksonXmlProperty(isAttribute = true, localName = "sample_rate") - public RTL2832TunerController.SampleRate getSampleRate() - { - return mSampleRate; - } - - public void setSampleRate(RTL2832TunerController.SampleRate sampleRate) - { - mSampleRate = sampleRate; - } - - /** - * Indicates if automatic correction of PPM from measured frequency error is enabled/disabled. - * @return true if auto-correction is enabled. - */ - @JacksonXmlProperty(isAttribute = true, localName = "auto_ppm_correction_enabled") - public boolean getAutoPPMCorrectionEnabled() - { - return mAutoPPMCorrection; - } - - /** - * Sets the enabled state for auto-correction of PPM from measured frequency error values. - * @param enabled - */ - public void setAutoPPMCorrectionEnabled(boolean enabled) - { - mAutoPPMCorrection = enabled; - } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java index 47620750..5589c510 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -1,90 +1,82 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.source.tuner.rtl.r820t; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; -import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate; -import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TGain; -import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; -import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; -import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TEmbeddedTuner.R820TGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TEmbeddedTuner.R820TLNAGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TEmbeddedTuner.R820TMixerGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TEmbeddedTuner.R820TVGAGain; +import io.github.dsheirer.source.tuner.ui.TunerEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; import javax.swing.JButton; -import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import javax.usb.UsbException; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; -public class R820TTunerEditor extends TunerConfigurationEditor +public class R820TTunerEditor extends TunerEditor { private final static Logger mLog = LoggerFactory.getLogger(R820TTunerEditor.class); - private static final long serialVersionUID = 1L; private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; + private JButton mTunerInfoButton; + private JComboBox mSampleRateCombo; + private JComboBox mMasterGainCombo; + private JComboBox mMixerGainCombo; + private JComboBox mLNAGainCombo; + private JComboBox mVGAGainCombo; - private R820TTunerController mController; - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JCheckBox mAutoPPMEnabled; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - private boolean mLoading; - - public R820TTunerEditor(TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner) + /** + * Constructs an instance + * @param userPreferences for wide-band recordings + * @param tunerManager for saving configurations + * @param discoveredTuner to edit + */ + public R820TTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) { - super(tunerConfigurationModel); - mController = (R820TTunerController)tuner.getController(); - + super(userPreferences, tunerManager, discoveredTuner); init(); + tunerStatusUpdated(); } - private R820TTunerConfiguration getConfiguration() + /** + * Access the R820T embedded tuner + * @return R820T tuner if there is a tuner, or null otherwise + */ + private R820TEmbeddedTuner getEmbeddedTuner() { - if(hasItem()) + if(hasTuner()) { - return (R820TTunerConfiguration)getItem(); + return (R820TEmbeddedTuner)getTuner().getController().getEmbeddedTuner(); } return null; @@ -92,324 +84,331 @@ public class R820TTunerEditor extends TunerConfigurationEditor private void init() { - setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][grow]")); + setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", + "[][][][][][][][][][][][][][][][grow]")); - add(new JLabel("R820T Tuner Configuration"), "span,align center"); + add(new JLabel("Tuner:")); + add(getTunerIdLabel()); + add(getTunerInfoButton()); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled(false); - mConfigurationName.addFocusListener(new FocusListener() - { - @Override - public void focusLost(FocusEvent e) - { - save(); - } + add(new JLabel("Status:")); + add(getTunerStatusLabel(), "wrap"); - @Override - public void focusGained(FocusEvent e) - { - } - }); + add(getButtonPanel(), "span,align left"); - add(new JLabel("Name:")); - add(mConfigurationName, "span 2"); + add(new JSeparator(), "span,growx,push"); - mTunerInfo = new JButton("Tuner Info"); - mTunerInfo.setEnabled(false); - mTunerInfo.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - JOptionPane.showMessageDialog(R820TTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE); - } - }); - add(mTunerInfo); + add(new JLabel("Frequency (MHz):")); + add(getFrequencyPanel(), "wrap"); - mComboSampleRate = new JComboBox<>(SampleRate.values()); - mComboSampleRate.setEnabled(false); - mComboSampleRate.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate(sampleRate); - save(); - } - catch(SourceException | LibUsbException eSampleRate) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage()); - - mLog.error("R820T Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate); - } - } - }); add(new JLabel("Sample Rate:")); - add(mComboSampleRate); + add(getSampleRateCombo(), "wrap"); - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel(0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1); //step + add(new JSeparator(), "span,growx,push"); + add(new JLabel("Gain Control"), "wrap"); - mFrequencyCorrection = new JSpinner(model); - mFrequencyCorrection.setEnabled(false); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits(1); - editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); - - mFrequencyCorrection.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection(value); - } - catch(SourceException e1) - { - mLog.error("Error setting frequency correction value", e1); - } - - save(); - } - }); - - add(new JLabel("PPM:")); - add(mFrequencyCorrection); - - add(new JLabel("")); //Space filler - add(new JLabel("")); //Space filler - add(new JLabel("")); //Space filler - mAutoPPMEnabled = new JCheckBox("PPM Auto-Correction"); - mAutoPPMEnabled.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - boolean enabled = mAutoPPMEnabled.isSelected(); - mController.getFrequencyErrorCorrectionManager().setEnabled(enabled); - save(); - } - }); - add(mAutoPPMEnabled); - - add(new JSeparator(JSeparator.HORIZONTAL), "span,grow"); - - /** - * Gain Controls - */ - add(new JLabel("Gain"), "wrap"); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox(R820TGain.values()); - mComboMasterGain.setEnabled(false); - mComboMasterGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - - mController.setGain((R820TGain)mComboMasterGain.getSelectedItem(), true); - - if(gain == R820TGain.MANUAL) - { - mComboMixerGain.setSelectedItem(gain.getMixerGain()); - mComboMixerGain.setEnabled(true); - - mComboLNAGain.setSelectedItem(gain.getLNAGain()); - mComboLNAGain.setEnabled(true); - - mComboVGAGain.setSelectedItem(gain.getVGAGain()); - mComboVGAGain.setEnabled(true); - } - else - { - mComboMixerGain.setEnabled(false); - mComboMixerGain.setSelectedItem(gain.getMixerGain()); - - mComboLNAGain.setEnabled(false); - mComboLNAGain.setSelectedItem(gain.getLNAGain()); - - mComboVGAGain.setEnabled(false); - mComboVGAGain.setSelectedItem(gain.getVGAGain()); - } - - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage()); - - mLog.error("R820T Tuner Controller - couldn't apply " - + "gain setting - ", e); - } - } - }); - mComboMasterGain.setToolTipText("Select AUTOMATIC for auto " - + "gain, MANUAL to enable
independent control of " - + "Mixer, LNA and Enhance gain
" - + "settings, or one of the individual gain settings for
" - + "semi-manual gain control"); add(new JLabel("Master:")); - add(mComboMasterGain); - - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox(R820TMixerGain.values()); - mComboMixerGain.setEnabled(false); - mComboMixerGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - R820TMixerGain mixerGain = - (R820TMixerGain)mComboMixerGain.getSelectedItem(); - - if(mixerGain == null) - { - mixerGain = DEFAULT_GAIN.getMixerGain(); - } - - if(mComboMixerGain.isEnabled()) - { - mController.setMixerGain(mixerGain, true); - } - - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage()); - - mLog.error("R820T Tuner Controller - couldn't apply mixer " - + "gain setting - ", e); - } - } - }); - mComboMixerGain.setToolTipText("Mixer Gain. Set master gain " - + "to MANUAL to enable adjustment"); + add(getMasterGainCombo(), "wrap"); add(new JLabel("Mixer:")); - add(mComboMixerGain); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox(R820TLNAGain.values()); - mComboLNAGain.setEnabled(false); - mComboLNAGain.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - try - { - R820TLNAGain lnaGain = - (R820TLNAGain)mComboLNAGain.getSelectedItem(); - - if(lnaGain == null) - { - lnaGain = DEFAULT_GAIN.getLNAGain(); - } - - if(mComboLNAGain.isEnabled()) - { - mController.setLNAGain(lnaGain, true); - } - - save(); - } - catch(UsbException e) - { - JOptionPane.showMessageDialog(R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage()); - - mLog.error("R820T Tuner Controller - couldn't apply LNA " - + "gain setting - ", e); - } - } - }); - mComboLNAGain.setToolTipText("LNA Gain. Set master gain " - + "to MANUAL to enable adjustment"); + add(getMixerGainCombo(), "wrap"); add(new JLabel("LNA:")); - add(mComboLNAGain); + add(getLNAGainCombo(), "wrap"); - /* VGA Gain Control */ - mComboVGAGain = new JComboBox(R820TVGAGain.values()); - mComboVGAGain.setEnabled(false); - mComboVGAGain.addActionListener(new ActionListener() + add(new JLabel("VGA:")); + add(getVGAGainCombo(), "wrap"); + } + + @Override + protected void tunerStatusUpdated() + { + setLoading(true); + + if(hasTuner()) { - @Override - public void actionPerformed(ActionEvent arg0) + getTunerIdLabel().setText(getTuner().getPreferredName()); + } + else + { + getTunerIdLabel().setText(getDiscoveredTuner().getId()); + } + + String status = getDiscoveredTuner().getTunerStatus().toString(); + if(getDiscoveredTuner().hasErrorMessage()) + { + status += " - " + getDiscoveredTuner().getErrorMessage(); + } + getTunerStatusLabel().setText(status); + getButtonPanel().updateControls(); + getFrequencyPanel().updateControls(); + + if(hasTuner()) + { + getTunerInfoButton().setEnabled(true); + getSampleRateCombo().setEnabled(true); + getSampleRateCombo().setSelectedItem(getConfiguration().getSampleRate()); + getMasterGainCombo().setEnabled(true); + R820TGain gain = getConfiguration().getMasterGain(); + getMasterGainCombo().setEnabled(true); + getMasterGainCombo().setSelectedItem(gain); + + if(gain == R820TGain.MANUAL) + { + getMixerGainCombo().setSelectedItem(getConfiguration().getMixerGain()); + getMixerGainCombo().setEnabled(true); + + getLNAGainCombo().setSelectedItem(getConfiguration().getLNAGain()); + getLNAGainCombo().setEnabled(true); + + getVGAGainCombo().setSelectedItem(getConfiguration().getVGAGain()); + getVGAGainCombo().setEnabled(true); + } + else + { + getMixerGainCombo().setEnabled(false); + getMixerGainCombo().setSelectedItem(gain.getMixerGain()); + + getLNAGainCombo().setEnabled(false); + getLNAGainCombo().setSelectedItem(gain.getLNAGain()); + + getVGAGainCombo().setEnabled(false); + getVGAGainCombo().setSelectedItem(gain.getVGAGain()); + } + } + else + { + getTunerInfoButton().setEnabled(false); + getSampleRateCombo().setEnabled(false); + getMasterGainCombo().setEnabled(false); + getMixerGainCombo().setEnabled(false); + getLNAGainCombo().setEnabled(false); + getVGAGainCombo().setEnabled(false); + } + + updateSampleRateToolTip(); + + setLoading(false); + } + + /** + * Hyperlink button that provides tuner information + */ + private JButton getTunerInfoButton() + { + if(mTunerInfoButton == null) + { + mTunerInfoButton = new JButton("Info"); + mTunerInfoButton.setEnabled(false); + mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(R820TTunerEditor.this, + getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE)); + } + + return mTunerInfoButton; + } + + private JComboBox getVGAGainCombo() + { + if(mVGAGainCombo == null) + { + mVGAGainCombo = new JComboBox<>(R820TVGAGain.values()); + mVGAGainCombo.setEnabled(false); + mVGAGainCombo.addActionListener(arg0 -> { try { - R820TVGAGain vgaGain = - (R820TVGAGain)mComboVGAGain.getSelectedItem(); + R820TVGAGain vgaGain = (R820TVGAGain) mVGAGainCombo.getSelectedItem(); if(vgaGain == null) { vgaGain = DEFAULT_GAIN.getVGAGain(); } - if(mComboVGAGain.isEnabled()) + if(mVGAGainCombo.isEnabled()) { - mController.setVGAGain(vgaGain, true); + getEmbeddedTuner().setVGAGain(vgaGain, true); } save(); } catch(UsbException e) { - JOptionPane.showMessageDialog(R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the VGA " - + "gain setting - " + e.getLocalizedMessage()); - - mLog.error("R820T Tuner Controller - couldn't apply VGA " - + "gain setting", e); + JOptionPane.showMessageDialog(R820TTunerEditor.this, "R820T Tuner Controller - " + + "couldn't apply the VGA gain setting - " + e.getLocalizedMessage()); + mLog.error("R820T Tuner Controller - couldn't apply VGA gain setting", e); } - } - }); - mComboVGAGain.setToolTipText("VGA Gain. Set master gain " - + "to MANUAL to enable adjustment"); - add(new JLabel("VGA:")); - add(mComboVGAGain); + }); + mVGAGainCombo.setToolTipText("VGA Gain. Set master gain to MANUAL to enable adjustment"); + } + + return mVGAGainCombo; + } + + private JComboBox getLNAGainCombo() + { + if(mLNAGainCombo == null) + { + mLNAGainCombo = new JComboBox<>(R820TLNAGain.values()); + mLNAGainCombo.setEnabled(false); + mLNAGainCombo.addActionListener(arg0 -> + { + try + { + R820TLNAGain lnaGain = (R820TLNAGain) mLNAGainCombo.getSelectedItem(); + + if(lnaGain == null) + { + lnaGain = DEFAULT_GAIN.getLNAGain(); + } + + if(mLNAGainCombo.isEnabled()) + { + getEmbeddedTuner().setLNAGain(lnaGain, true); + } + + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(R820TTunerEditor.this, "R820T Tuner Controller - " + + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage()); + mLog.error("R820T Tuner Controller - couldn't apply LNA " + "gain setting - ", e); + } + }); + mLNAGainCombo.setToolTipText("LNA Gain. Set master gain to MANUAL to enable adjustment"); + } + + return mLNAGainCombo; + } + + private JComboBox getSampleRateCombo() + { + if(mSampleRateCombo == null) + { + mSampleRateCombo = new JComboBox<>(SampleRate.values()); + mSampleRateCombo.setEnabled(false); + mSampleRateCombo.addActionListener(e -> + { + if(!isLoading()) + { + SampleRate sampleRate = (SampleRate) mSampleRateCombo.getSelectedItem(); + + try + { + getTuner().getController().setSampleRate(sampleRate); + save(); + } + catch(SourceException | LibUsbException eSampleRate) + { + JOptionPane.showMessageDialog(R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the sample rate setting [" + + sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage()); + + mLog.error("R820T Tuner Controller - couldn't apply sample rate setting [" + sampleRate.getLabel() + + "]", eSampleRate); + } + } + }); + } + + return mSampleRateCombo; + } + + private JComboBox getMixerGainCombo() + { + if(mMixerGainCombo == null) + { + mMixerGainCombo = new JComboBox<>(R820TMixerGain.values()); + mMixerGainCombo.setEnabled(false); + mMixerGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + R820TMixerGain mixerGain = (R820TMixerGain) mMixerGainCombo.getSelectedItem(); + + if(mixerGain == null) + { + mixerGain = DEFAULT_GAIN.getMixerGain(); + } + + if(mMixerGainCombo.isEnabled()) + { + getEmbeddedTuner().setMixerGain(mixerGain, true); + } + + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(R820TTunerEditor.this, "R820T Tuner Controller - " + + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage()); + + mLog.error("R820T Tuner Controller - couldn't apply mixer gain setting - ", e); + } + } + }); + mMixerGainCombo.setToolTipText("Mixer Gain. Set master gain to MANUAL to enable adjustment"); + } + + return mMixerGainCombo; + } + + private JComboBox getMasterGainCombo() + { + if(mMasterGainCombo == null) + { + mMasterGainCombo = new JComboBox<>(R820TGain.values()); + mMasterGainCombo.setEnabled(false); + mMasterGainCombo.addActionListener(arg0 -> + { + if(!isLoading()) + { + try + { + R820TGain gain = (R820TGain)getMasterGainCombo().getSelectedItem(); + getEmbeddedTuner().setGain((R820TGain)getMasterGainCombo().getSelectedItem(), true); + + if(gain == R820TGain.MANUAL) + { + getMixerGainCombo().setSelectedItem(gain.getMixerGain()); + getMixerGainCombo().setEnabled(true); + + getLNAGainCombo().setSelectedItem(gain.getLNAGain()); + getLNAGainCombo().setEnabled(true); + + getVGAGainCombo().setSelectedItem(gain.getVGAGain()); + getVGAGainCombo().setEnabled(true); + } + else + { + getMixerGainCombo().setEnabled(false); + getMixerGainCombo().setSelectedItem(gain.getMixerGain()); + + getLNAGainCombo().setEnabled(false); + getLNAGainCombo().setSelectedItem(gain.getLNAGain()); + + getVGAGainCombo().setEnabled(false); + getVGAGainCombo().setSelectedItem(gain.getVGAGain()); + } + + save(); + } + catch(UsbException e) + { + JOptionPane.showMessageDialog(R820TTunerEditor.this, "R820T Tuner Controller - " + + "couldn't apply the gain setting - " + e.getLocalizedMessage()); + mLog.error("R820T Tuner Controller - couldn't apply gain setting - ", e); + } + } + }); + mMasterGainCombo.setToolTipText("Select AUTOMATIC for auto gain, MANUAL to enable
" + + "independent control of Mixer, LNA and Enhance gain
settings, or one of the " + + "individual gain settings for
semi-manual gain control"); + } + + return mMasterGainCombo; } /** @@ -417,85 +416,31 @@ public class R820TTunerEditor extends TunerConfigurationEditor */ private void updateSampleRateToolTip() { - if(mController.isLocked()) + if(hasTuner() && getTuner().getTunerController().isLocked()) { - mComboSampleRate.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + } + else if(hasTuner()) + { + getSampleRateCombo().setToolTipText("Select a sample rate for the tuner"); } else { - mComboSampleRate.setToolTipText("Select a sample rate for the tuner"); + getSampleRateCombo().setToolTipText("No tuner available"); } } @Override public void setTunerLockState(boolean locked) { - mComboSampleRate.setEnabled(!locked); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled(boolean enabled) - { - if(mConfigurationName.isEnabled() != enabled) - { - mConfigurationName.setEnabled(enabled); - } - - if(mTunerInfo.isEnabled() != enabled) - { - mTunerInfo.setEnabled(enabled); - } - - if(mFrequencyCorrection.isEnabled() != enabled) - { - mFrequencyCorrection.setEnabled(enabled); - } - - if(mAutoPPMEnabled.isEnabled() != enabled) - { - mAutoPPMEnabled.setEnabled(enabled); - } - - updateSampleRateToolTip(); - - if(mController.isLocked()) - { - mComboSampleRate.setEnabled(false); - } - else if(mComboSampleRate.isEnabled() != enabled) - { - mComboSampleRate.setEnabled(enabled); - } - - if(mComboMasterGain.isEnabled() != enabled) - { - mComboMasterGain.setEnabled(enabled); - } - - if(mComboMixerGain.isEnabled() != enabled) - { - mComboMixerGain.setEnabled(enabled); - } - - if(mComboLNAGain.isEnabled() != enabled) - { - mComboLNAGain.setEnabled(enabled); - } - - if(mComboVGAGain.isEnabled() != enabled) - { - mComboVGAGain.setEnabled(enabled); - } + super.setTunerLockState(locked); + mSampleRateCombo.setEnabled(!locked); } private String getTunerInfo() { StringBuilder sb = new StringBuilder(); - - RTL2832TunerController.Descriptor descriptor = mController.getDescriptor(); - + RTL2832TunerController.Descriptor descriptor = getTuner().getController().getDescriptor(); sb.append("

RTL-2832 with R820T Tuner

"); if(descriptor == null) @@ -537,76 +482,25 @@ public class R820TTunerEditor extends TunerConfigurationEditor @Override public void save() { - if(hasItem() && !mLoading) + if(hasConfiguration() && !isLoading()) { R820TTunerConfiguration config = getConfiguration(); - config.setName(mConfigurationName.getText()); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); + config.setFrequency(getFrequencyControl().getFrequency()); + double value = ((SpinnerNumberModel)getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); config.setFrequencyCorrection(value); + config.setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected()); - config.setAutoPPMCorrectionEnabled(mAutoPPMEnabled.isSelected()); - - config.setSampleRate((SampleRate)mComboSampleRate.getSelectedItem()); - - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); + config.setSampleRate((SampleRate)getSampleRateCombo().getSelectedItem()); + R820TGain gain = (R820TGain)getMasterGainCombo().getSelectedItem(); config.setMasterGain(gain); - - R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); + R820TMixerGain mixerGain = (R820TMixerGain)getMixerGainCombo().getSelectedItem(); config.setMixerGain(mixerGain); - - R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); + R820TLNAGain lnaGain = (R820TLNAGain)getLNAGainCombo().getSelectedItem(); config.setLNAGain(lnaGain); - - R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); + R820TVGAGain vgaGain = (R820TVGAGain)getVGAGainCombo().getSelectedItem(); config.setVGAGain(vgaGain); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent(getConfiguration(), TunerConfigurationEvent.Event.CHANGE)); + saveConfiguration(); } } - - @Override - public void setItem(TunerConfiguration tunerConfiguration) - { - super.setItem(tunerConfiguration); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if(hasItem()) - { - R820TTunerConfiguration config = getConfiguration(); - - if(tunerConfiguration.isAssigned()) - { - setControlsEnabled(true); - mConfigurationName.setText(config.getName()); - mFrequencyCorrection.setValue(config.getFrequencyCorrection()); - mAutoPPMEnabled.setSelected(config.getAutoPPMCorrectionEnabled()); - mComboSampleRate.setSelectedItem(config.getSampleRate()); - mComboMasterGain.setSelectedItem(config.getMasterGain()); - mComboMixerGain.setSelectedItem(config.getMixerGain()); - mComboLNAGain.setSelectedItem(config.getLNAGain()); - mComboVGAGain.setSelectedItem(config.getVGAGain()); - - //Update enabled state to reflect when frequency and sample rate controls are locked - mComboSampleRate.setEnabled(!mController.isLocked()); - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(config.getName()); - } - } - else - { - setControlsEnabled(false); - mConfigurationName.setText(""); - } - - mLoading = false; - } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java b/src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java index 93d215a3..631c78fc 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java @@ -1,27 +1,24 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.source.tuner.test; -import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; @@ -38,9 +35,9 @@ public class TestTuner extends Tuner private static int mInstanceCounter = 1; private final int mInstanceID = mInstanceCounter++; - public TestTuner(UserPreferences userPreferences) + public TestTuner(ITunerErrorListener tunerErrorListener) { - super("Test Tuner", new TestTunerController(), userPreferences); + super(new TestTunerController(), tunerErrorListener); } /** @@ -51,10 +48,16 @@ public class TestTuner extends Tuner return (TestTunerController)super.getTunerController(); } + @Override + public String getPreferredName() + { + return "Test Tuner-" + mInstanceID; + } + @Override public String getUniqueID() { - return getName() + "-" + mInstanceID; + return getPreferredName(); } @Override @@ -66,7 +69,7 @@ public class TestTuner extends Tuner @Override public TunerType getTunerType() { - return TunerClass.TEST_TUNER.getTunerType(); + return TunerType.TEST; } @Override diff --git a/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java index a3cd4da4..c1830d50 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java @@ -21,7 +21,9 @@ package io.github.dsheirer.source.tuner.test; import io.github.dsheirer.buffer.INativeBuffer; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.LoggingTunerErrorListener; import io.github.dsheirer.source.tuner.TunerController; +import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +49,12 @@ public class TestTunerController extends TunerController */ public TestTunerController() { - super(MINIMUM_FREQUENCY, MAXIMUM_FREQUENCY, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH_PERCENTAGE); + super(new LoggingTunerErrorListener()); + + setMinimumFrequency(MINIMUM_FREQUENCY); + setMaximumFrequency(MAXIMUM_FREQUENCY); + setMiddleUnusableHalfBandwidth(DC_NOISE_BANDWIDTH); + setUsableBandwidthPercentage(USABLE_BANDWIDTH_PERCENTAGE); int sweepRate = 0; //Hz per interval // long initialToneFrequency = SAMPLE_RATE / 2 + 100; @@ -72,9 +79,21 @@ public class TestTunerController extends TunerController } @Override - public void dispose() + public void start() throws SourceException { - //no-op + //No-op + } + + @Override + public void stop() + { + //No-op + } + + @Override + public TunerType getTunerType() + { + return TunerType.TEST; } @Override diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java new file mode 100644 index 00000000..a3f6efb6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java @@ -0,0 +1,128 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.ui; + +import io.github.dsheirer.gui.editor.Editor; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.tuner.configuration.TunerConfigurationFactory; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.manager.TunerStatus; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * Editor for Discovered Tuners that displays custom tuner editor for the selected discovered tuner + */ +public class DiscoveredTunerEditor extends Editor implements IDiscoveredTunerStatusListener +{ + private static final long serialVersionUID = 1L; + private static final Logger mLog = LoggerFactory.getLogger(DiscoveredTunerEditor.class); + private UserPreferences mUserPreferences; + private TunerManager mTunerManager; + private JPanel mEmptyEditor = new EmptyTunerEditor(); + private JPanel mEditor = mEmptyEditor; + private JScrollPane mEditorScroller; + + /** + * Constructs an instance + * @param userPreferences to use for starting wideband recordings sourced by a tuner + * @param tunerManager for creating tuner editors + */ + public DiscoveredTunerEditor(UserPreferences userPreferences, TunerManager tunerManager) + { + mUserPreferences = userPreferences; + mTunerManager = tunerManager; + init(); + } + + /** + * Updates the lock state of the tuner editor so that lockable controls are updated + * @param locked true if the tuner is locked and the controls should be locked. + */ + public void setTunerLockState(boolean locked) + { + if(mEditor instanceof TunerEditor tunerEditor) + { + tunerEditor.setTunerLockState(locked); + } + } + + public void init() + { + setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); + mEditorScroller = new JScrollPane(mEditor); + add(mEditorScroller); + } + + @Override + public void save() + { + //Unused + } + + @Override + public void setItem(DiscoveredTuner tuner) + { + if(hasItem()) + { + getItem().removeTunerStatusListener(this); + } + + //Cleanup previously selected tuner + if(hasItem() && mEditor instanceof TunerEditor tunerEditor) + { + tunerEditor.dispose(); + } + + super.setItem(tuner); + + if(hasItem()) + { + mEditor = TunerConfigurationFactory.getEditor(mUserPreferences, getItem(), mTunerManager); + getItem().addTunerStatusListener(this); + } + else + { + mEditor = mEmptyEditor; + } + + remove(mEditorScroller); + mEditorScroller = new JScrollPane(mEditor); + add(mEditorScroller); + revalidate(); + repaint(); + } + + @Override + public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current) + { + //If this is the currently displayed tuner, set it again to re-render the editor + if(hasItem() && getItem().equals(discoveredTuner)) + { + setItem(discoveredTuner); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java new file mode 100644 index 00000000..32592427 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java @@ -0,0 +1,500 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.ui; + +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.source.tuner.Tuner; +import io.github.dsheirer.source.tuner.TunerEvent; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.DiscoveredUSBTuner; +import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener; +import io.github.dsheirer.source.tuner.manager.TunerStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.table.AbstractTableModel; +import java.awt.EventQueue; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Model for discovered tuners + */ +public class DiscoveredTunerModel extends AbstractTableModel implements Listener, + IDiscoveredTunerStatusListener +{ + private static final long serialVersionUID = 1L; + private static final Logger mLog = LoggerFactory.getLogger(DiscoveredTunerModel.class); + + //Model columns + public static final int COLUMN_TUNER_STATUS = 0; + public static final int COLUMN_TUNER_CLASS = 1; + public static final int COLUMN_TUNER_TYPE = 2; + public static final int COLUMN_FREQUENCY = 3; + public static final int COLUMN_CHANNEL_COUNT = 4; + private static final String MHZ = " MHz"; + private static final String[] COLUMN_HEADERS = {"Status","Class", "Type", "Frequency", "Channels"}; + + private List mDiscoveredTuners = new CopyOnWriteArrayList<>(); + private List> mTunerEventListeners = new ArrayList<>(); + private DecimalFormat mFrequencyFormat = new DecimalFormat("0.00000"); + private Lock mLock = new ReentrantLock(); + + + /** + * Constructs an instance + */ + public DiscoveredTunerModel() + { + } + + /** + * List of currently enabled and available tuners + */ + public List getAvailableTuners() + { + return mDiscoveredTuners.stream().filter(discoveredTuner -> discoveredTuner.hasTuner()).toList(); + } + + /** + * Find the discovered tuner that matches the instantiated tuner + * @param tuner to match + * @return matching discovered tuner + */ + private DiscoveredTuner getDiscoveredTuner(Tuner tuner) + { + DiscoveredTuner match = null; + + mLock.lock(); + + try + { + for(DiscoveredTuner discoveredTuner: mDiscoveredTuners) + { + if(discoveredTuner.hasTuner() && discoveredTuner.getTuner().equals(tuner)) + { + match = discoveredTuner; + break; + } + } + } + finally + { + mLock.unlock(); + } + + return match; + } + + /** + * Access the discovered tuner at the specified row index + * @param index to lookup + * @return discovered tuner or null + */ + public DiscoveredTuner getDiscoveredTuner(int index) + { + mLock.lock(); + + try + { + if(index < mDiscoveredTuners.size()) + { + return mDiscoveredTuners.get(index); + } + } + finally + { + mLock.unlock(); + } + + return null; + } + + /** + * Adds the Tuner to this model + */ + public void addDiscoveredTuner(DiscoveredTuner discoveredTuner) + { + mLock.lock(); + + try + { + if(!mDiscoveredTuners.contains(discoveredTuner)) + { + mDiscoveredTuners.add(discoveredTuner); + int index = mDiscoveredTuners.indexOf(discoveredTuner); + EventQueue.invokeLater(() -> fireTableRowsInserted(index, index)); + if(discoveredTuner.hasTuner()) + { + discoveredTuner.getTuner().addTunerEventListener(this); + } + discoveredTuner.addTunerStatusListener(this); + } + } + finally + { + mLock.unlock(); + } + } + + /** + * Stops and removes all discovered tuners, in preparation for shutdown. + */ + public void releaseDiscoveredTuners() + { + mLock.lock(); + + try + { + List discoveredTuners = new ArrayList<>(mDiscoveredTuners); + + for(DiscoveredTuner discoveredTuner: discoveredTuners) + { + if(discoveredTuner.hasTuner()) + { + discoveredTuner.getTuner().removeTunerEventListener(this); + } + + removeDiscoveredTuner(discoveredTuner); + } + } + finally + { + mLock.unlock(); + } + } + + /** + * Removes the Tuner from this model + */ + public void removeDiscoveredTuner(DiscoveredTuner discoveredTuner) + { + mLock.lock(); + + try + { + if(mDiscoveredTuners.contains(discoveredTuner)) + { + int index = mDiscoveredTuners.indexOf(discoveredTuner); + mDiscoveredTuners.remove(discoveredTuner); + EventQueue.invokeLater(() -> fireTableRowsDeleted(index, index)); + discoveredTuner.stop(); + } + } + finally + { + mLock.unlock(); + } + } + + /** + * Indicates if the discovered tuner is still being managed by this tuner model and hasn't been removed. + */ + public boolean hasTuner(DiscoveredTuner discoveredTuner) + { + return mDiscoveredTuners.contains(discoveredTuner); + } + + /** + * Indicates if this model currently has the discovered tuner that is plugged into the specified bus and port. + * @param bus number + * @param port number + * @return true if it already has the discovered device + */ + public boolean hasUsbTuner(int bus, int port) + { + boolean hasDevice; + mLock.lock(); + + try + { + hasDevice = mDiscoveredTuners.stream() + .filter(tuner -> tuner instanceof DiscoveredUSBTuner usbTuner && usbTuner.isAt(bus, port)) + .findFirst() + .isPresent(); + } + finally + { + mLock.unlock(); + } + + return hasDevice; + } + + /** + * Removes the USB tuner at bus and port number + * @param bus usb + * @param port usb + */ + public void removeUsbTuner(int bus, int port) + { + mLock.lock(); + + try + { + DiscoveredTuner discoveredTuner = mDiscoveredTuners.stream() + .filter(tuner -> tuner instanceof DiscoveredUSBTuner usbTuner && + usbTuner.isAt(bus, port)).findFirst().get(); + + if(discoveredTuner != null) + { + removeDiscoveredTuner(discoveredTuner); + } + } + finally + { + mLock.unlock(); + } + } + + /** + * Adds a tuner event listener + */ + public void addListener(Listener listener) + { + mTunerEventListeners.add(listener); + } + + /** + * Removes a tuner event listener + */ + public void removeListener(Listener listener) + { + mTunerEventListeners.remove(listener); + } + + public void broadcast(TunerEvent event) + { + for(Listener listener : mTunerEventListeners) + { + listener.receive(event); + } + } + + /** + * Requests to display the first tuner in this model. Invoke this method + * after all listeners have registered and tuners have been added to this + * model, in order to inform the primary display to use the first tuner. + */ + public void requestFirstTunerDisplay() + { +//TODO: move this out of the model ... is not part of the scope of this model +// SystemProperties properties = SystemProperties.getInstance(); +// boolean enabled = properties.get(SpectralDisplayPanel.SPECTRAL_DISPLAY_ENABLED, true); +// +// if(enabled && mDiscoveredTuners.size() > 0) +// { +// //Hack: the airspy tuner would lockup aperiodically and refuse to produce +// //transfer buffers ... delaying registering for buffers for 500 ms seems +// //to allow the airspy to stabilize before we start asking for samples. +// ThreadPool.SCHEDULED.schedule(new Runnable() +// { +// @Override +// public void run() +// { +// broadcast(new TunerEvent(mDiscoveredTuners.get(0), Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); +// } +// }, 500, TimeUnit.MILLISECONDS); +// } +// else +// { +// broadcast(new TunerEvent(null, Event.CLEAR_MAIN_SPECTRAL_DISPLAY)); +// } + } + + @Override + public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current) + { + if(current == TunerStatus.ENABLED && discoveredTuner.hasTuner()) + { + discoveredTuner.getTuner().addTunerEventListener(this); + } + + int row = mDiscoveredTuners.indexOf(discoveredTuner); + EventQueue.invokeLater(() -> fireTableRowsUpdated(row, row)); + } + + @Override + public void receive(TunerEvent event) + { + if(event.hasTuner()) + { + mLock.lock(); + + try + { + DiscoveredTuner matching = getDiscoveredTuner(event.getTuner()); + int index = mDiscoveredTuners.indexOf(matching); + + if(index >= 0) + { + switch(event.getEvent()) + { + case UPDATE_CHANNEL_COUNT: + EventQueue.invokeLater(() -> fireTableCellUpdated(index, COLUMN_CHANNEL_COUNT)); + break; + case UPDATE_FREQUENCY: + EventQueue.invokeLater(() -> fireTableCellUpdated(index, COLUMN_FREQUENCY)); + break; + case NOTIFICATION_ERROR_STATE: + EventQueue.invokeLater(() -> fireTableRowsUpdated(index, index)); + break; + default: + break; + } + } + } + finally + { + mLock.unlock(); + } + } + else + { + mLog.error("Got a tuner event without a tuner - " + event); + } + + broadcast(event); + } + + @Override + public int getRowCount() + { + return mDiscoveredTuners.size(); + } + + @Override + public int getColumnCount() + { + return COLUMN_HEADERS.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) + { + if(rowIndex < mDiscoveredTuners.size()) + { + DiscoveredTuner discoveredTuner = mDiscoveredTuners.get(rowIndex); + + switch(columnIndex) + { + case COLUMN_TUNER_STATUS: + return discoveredTuner.getTunerStatus(); + case COLUMN_TUNER_CLASS: + return discoveredTuner.getTunerClass().toString(); + case COLUMN_TUNER_TYPE: + if(discoveredTuner.hasTuner()) + { + return discoveredTuner.getTuner().getTunerType().getLabel(); + } + else + { + return ""; + } +// case COLUMN_TUNER_ID: +// if(discoveredTuner.hasTuner()) +// { +// return discoveredTuner.getTuner().getUniqueID(); +// } +// else +// { +// return discoveredTuner.getId(); +// } +// case COLUMN_SAMPLE_RATE: +// if(discoveredTuner.hasTuner()) +// { +// double sampleRate = discoveredTuner.getTuner().getTunerController().getSampleRate(); +// return mSampleRateFormat.format(sampleRate / 1E6D) + MHZ; +// } +// else +// { +// return ""; +// } + case COLUMN_FREQUENCY: + if(discoveredTuner.hasTuner()) + { + long frequency = discoveredTuner.getTuner().getTunerController().getFrequency(); + return mFrequencyFormat.format(frequency / 1E6D) + MHZ; + } + else + { + return ""; + } + case COLUMN_CHANNEL_COUNT: + if(discoveredTuner.hasTuner()) + { + int channelCount = discoveredTuner.getTuner().getChannelSourceManager().getTunerChannelCount(); + return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)"); + } + else + { + return ""; + } +// case COLUMN_FREQUENCY_ERROR: +// if(discoveredTuner.hasTuner()) +// { +// double ppm = discoveredTuner.getTuner().getTunerController().getFrequencyCorrection(); +// return mFrequencyErrorPPMFormat.format(ppm); +// } +// else +// { +// return ""; +// } +// case COLUMN_MEASURED_FREQUENCY_ERROR: +// if(discoveredTuner.hasTuner()) +// { +// if(discoveredTuner.getTuner().getTunerController().hasMeasuredFrequencyError()) +// { +// StringBuilder sb = new StringBuilder(); +// sb.append(discoveredTuner.getTuner().getTunerController().getMeasuredFrequencyError()); +// sb.append("Hz ("); +// sb.append(mFrequencyErrorPPMFormat.format(discoveredTuner.getTuner().getTunerController().getPPMFrequencyError())); +// sb.append("ppm)"); +// return sb.toString(); +// } +// } +// return ""; +// case COLUMN_ERROR_OR_SPECTRAL_DISPLAY_NEW: +// if(discoveredTuner.hasErrorMessage()) +// { +// return discoveredTuner.getErrorMessage(); +// } +// else if(discoveredTuner.hasTuner()) +// { +// return "New"; +// } +// return ""; + default: + break; + } + } + + return null; + } + + @Override + public String getColumnName(int columnIndex) + { + return COLUMN_HEADERS[columnIndex]; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/EmptyTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/ui/EmptyTunerEditor.java new file mode 100644 index 00000000..98a5e556 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/EmptyTunerEditor.java @@ -0,0 +1,42 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.ui; + +import net.miginfocom.swing.MigLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + * Empty tuner editor panel + */ +public class EmptyTunerEditor extends JPanel +{ + private static final long serialVersionUID = 1L; + + /** + * Constructs an instance + */ + public EmptyTunerEditor() + { + setLayout(new MigLayout("insets 0 0 0 0", "[center,grow]", "[center,grow]")); + add(new JLabel("No tuner selected"), "wrap"); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java new file mode 100644 index 00000000..3a85ad31 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java @@ -0,0 +1,707 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.ui; + +import io.github.dsheirer.gui.control.JFrequencyControl; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.record.wave.IRecordingStatusListener; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.source.ISourceEventProcessor; +import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.Tuner; +import io.github.dsheirer.source.tuner.TunerEvent; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.manager.TunerStatus; +import io.github.dsheirer.util.SwingUtils; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JToggleButton; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.EventQueue; +import java.text.CharacterIterator; +import java.text.DecimalFormat; +import java.text.StringCharacterIterator; + +/** + * Base tuner configuration editor. + */ +public abstract class TunerEditor extends JPanel + implements IDiscoveredTunerStatusListener, Listener +{ + private Logger mLog = LoggerFactory.getLogger(TunerEditor.class); + private static final String BUTTON_STATUS_ENABLE = "Enable"; + private static final String BUTTON_STATUS_DISABLE = "Disable"; + private static final String BUTTON_STATUS_RESET = "Reset Error"; + private static final long serialVersionUID = 1L; + private UserPreferences mUserPreferences; + private TunerManager mTunerManager; + private DiscoveredTuner mDiscoveredTuner; + private C mTunerConfiguration; + private FrequencyAndCorrectionChangeListener mFrequencyAndCorrectionChangeListener = new FrequencyAndCorrectionChangeListener(); + private JFrequencyControl mFrequencyControl; + private JSpinner mFrequencyCorrectionSpinner; + private JButton mChangeStatusButton; + private JButton mViewSpectrumButton; + private JButton mNewSpectrumButton; + private JToggleButton mRecordButton; + private ButtonPanel mButtonsPanel; + private FrequencyPanel mFrequencyPanel; + private JLabel mTunerIdLabel; + private JCheckBox mAutoPPMCheckBox; + private JLabel mMeasuredPPMLabel; + private JLabel mRecordingStatusLabel; + private JLabel mTunerStatusLabel; + private JLabel mTunerLockedStatusLabel; + private boolean mLoading = false; + + /** + * Constructs an instance + * @param tunerManager for requesting configuration saves. + */ + public TunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner) + { + mUserPreferences = userPreferences; + mTunerManager = tunerManager; + mDiscoveredTuner = discoveredTuner; + + if(mDiscoveredTuner != null && mDiscoveredTuner.hasTunerConfiguration()) + { + mTunerConfiguration = (C)mDiscoveredTuner.getTunerConfiguration(); + } + + if(mDiscoveredTuner != null) + { + mDiscoveredTuner.addTunerStatusListener(this); + + if(mDiscoveredTuner.hasTuner()) + { + mDiscoveredTuner.getTuner().addTunerEventListener(this); + } + } + } + + /** + * Indicates if the controls are currently being loaded with values. + */ + protected boolean isLoading() + { + return mLoading; + } + + /** + * Changes the loading status + */ + protected void setLoading(boolean loading) + { + mLoading = loading; + } + + /** + * Measured PPM value received from any decoders that may be running. + */ + protected JLabel getMeasuredPPMLabel() + { + if(mMeasuredPPMLabel == null) + { + mMeasuredPPMLabel = new JLabel(""); + mMeasuredPPMLabel.setToolTipText("Displays the measured frequency error and PPM when provided by compatible channel decoders"); + } + + return mMeasuredPPMLabel; + } + + /** + * Tuner locked status label that can be turned on/off depending on tuner lock state. + */ + protected JLabel getTunerLockedStatusLabel() + { + if(mTunerLockedStatusLabel == null) + { + mTunerLockedStatusLabel = new JLabel("Channel(s) active - frequency and sample rate controls are locked"); + mTunerLockedStatusLabel.setToolTipText("Indicates that the tuner is providing channel(s) and you can't " + + "change the tuner frequency or sample rate"); + mTunerLockedStatusLabel.setVisible(false); + } + + return mTunerLockedStatusLabel; + } + + /** + * Tuner status label + */ + protected JLabel getTunerStatusLabel() + { + if(mTunerStatusLabel == null) + { + mTunerStatusLabel = new JLabel(" "); + } + + return mTunerStatusLabel; + } + + + /** + * Label to display current file size for a wide-band recording + */ + protected JLabel getRecordingStatusLabel() + { + if(mRecordingStatusLabel == null) + { + mRecordingStatusLabel = new JLabel(" "); + mRecordingStatusLabel.setToolTipText("Shows the status of the latest baseband recording when active"); + mRecordingStatusLabel.setVisible(false); + } + + return mRecordingStatusLabel; + } + + /** + * Check box for enable/disable automatic PPM adjustment from decoder(s) frequency error feedback. + */ + protected JCheckBox getAutoPPMCheckBox() + { + if(mAutoPPMCheckBox == null) + { + mAutoPPMCheckBox = new JCheckBox("Enable decoder(s) to auto-adjust PPM"); + mAutoPPMCheckBox.setToolTipText("Allow decoders to measure channel frequency error and correct tuner PPM"); + mAutoPPMCheckBox.addActionListener(e -> + { + if(!isLoading()) + { + Tuner tuner = getTuner(); + if(tuner != null) + { + boolean enabled = getAutoPPMCheckBox().isSelected(); + getTuner().getTunerController().getFrequencyErrorCorrectionManager().setEnabled(enabled); + save(); + } + } + }); + } + + return mAutoPPMCheckBox; + } + + /** + * Label for displaying the tuner ID + */ + protected JLabel getTunerIdLabel() + { + if(mTunerIdLabel == null) + { + mTunerIdLabel = new JLabel(" "); + } + + return mTunerIdLabel; + } + + protected FrequencyPanel getFrequencyPanel() + { + if(mFrequencyPanel == null) + { + mFrequencyPanel = new FrequencyPanel(); + mFrequencyPanel.setToolTipText("Tuner frequency and PPM controls"); + } + + return mFrequencyPanel; + } + + protected JSpinner getFrequencyCorrectionSpinner() + { + if(mFrequencyCorrectionSpinner == null) + { + SpinnerModel model = new SpinnerNumberModel(0.0, -1000.0, 1000.0, 0.1); + mFrequencyCorrectionSpinner = new JSpinner(model); + mFrequencyCorrectionSpinner.setToolTipText("Adjust the PPM value to compensate for tuner frequency error"); + mFrequencyCorrectionSpinner.setEnabled(false); + JSpinner.NumberEditor editor = (JSpinner.NumberEditor) mFrequencyCorrectionSpinner.getEditor(); + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits(1); + editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER); + mFrequencyCorrectionSpinner.addChangeListener(mFrequencyAndCorrectionChangeListener); + } + + return mFrequencyCorrectionSpinner; + } + + protected ButtonPanel getButtonPanel() + { + if(mButtonsPanel == null) + { + mButtonsPanel = new ButtonPanel(); + mButtonsPanel.setToolTipText("Button controls for the selected tuner"); + } + + return mButtonsPanel; + } + + protected JFrequencyControl getFrequencyControl() + { + if(mFrequencyControl == null) + { + mFrequencyControl = new JFrequencyControl(); + } + + return mFrequencyControl; + } + + /** + * Button requesting to show tuner in new spectral display + */ + protected JButton getNewSpectrumButton() + { + if(mNewSpectrumButton == null) + { + mNewSpectrumButton = new JButton("New Spectrum Display"); + mNewSpectrumButton.setToolTipText("Show this tuner in a new (separate) spectral display window"); + mNewSpectrumButton.addActionListener(e -> + { + Tuner tuner = getTuner(); + + if(tuner != null) + { + mTunerManager.getDiscoveredTunerModel().broadcast(new TunerEvent(tuner, + TunerEvent.Event.REQUEST_NEW_SPECTRAL_DISPLAY)); + } + }); + } + + return mNewSpectrumButton; + } + + /** + * Button requesting to show tuner in main spectral display + */ + protected JButton getViewSpectrumButton() + { + if(mViewSpectrumButton == null) + { + mViewSpectrumButton = new JButton("View Spectrum"); + mViewSpectrumButton.setToolTipText("Show this tuner in the spectral display"); + mViewSpectrumButton.addActionListener(e -> + { + Tuner tuner = getTuner(); + + if(tuner != null) + { + mTunerManager.getDiscoveredTunerModel().broadcast(new TunerEvent(tuner, + TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); + } + }); + } + + return mViewSpectrumButton; + } + + /** + * Button to change enable, disable, or error restart status. + */ + protected JButton getChangeStatusButton() + { + if(mChangeStatusButton == null) + { + mChangeStatusButton = new JButton(BUTTON_STATUS_ENABLE); + mChangeStatusButton.setToolTipText("Enable or disable the tuner for use by sdrtrunk, or attempt to reset a tuner error"); + mChangeStatusButton.addActionListener(e -> + { + switch(getChangeStatusButton().getText()) + { + case BUTTON_STATUS_DISABLE: + mLog.info("Disabling " + getDiscoveredTuner().getTunerClass() + " tuner"); + getDiscoveredTuner().setEnabled(false); + break; + case BUTTON_STATUS_ENABLE: + mLog.info("Enabling " + getDiscoveredTuner().getTunerClass() + " tuner"); + getDiscoveredTuner().setEnabled(true); + break; + case BUTTON_STATUS_RESET: + mLog.info("Resetting " + getDiscoveredTuner().getTunerClass() + " tuner"); + getDiscoveredTuner().reset(); + break; + default: + mLog.info("None matched"); + } + }); + } + + return mChangeStatusButton; + } + + /** + * Turns off the recorder, if it's currently recording. + */ + protected void turnOffRecorder() + { + if(getRecordButton().isSelected()) + { + getRecordButton().setSelected(false); + } + } + + /** + * Records the tuner's wide-band sample stream. + */ + protected JToggleButton getRecordButton() + { + if(mRecordButton == null) + { + mRecordButton = new JToggleButton("Record"); + mRecordButton.setToolTipText("Create a baseband recording for this tuner"); + mRecordButton.setEnabled(false); + mRecordButton.addActionListener(e -> + { + if(hasTuner()) + { + if(getRecordButton().isSelected()) + { + getRecordingStatusLabel().setVisible(true); + getRecordingStatusLabel().setText(" "); + getTuner().getTunerController().startRecorder(mUserPreferences, new RecordingStatusListener(), + getDiscoveredTuner().getTunerClass().name()); + } + else + { + getTuner().getTunerController().stopRecorder(); + } + } + }); + } + + return mRecordButton; + } + + protected abstract void save(); + + /** + * Access the tuner configuration + */ + protected C getConfiguration() + { + return mTunerConfiguration; + } + + /** + * Indicates if this editor has a tuner configuration + */ + protected boolean hasConfiguration() + { + return mTunerConfiguration != null; + } + + /** + * Discovered tuner for this editor + */ + protected DiscoveredTuner getDiscoveredTuner() + { + return mDiscoveredTuner; + } + + /** + * Notification that the status of the discovered tuner has changed and that the editor implementation must + * refresh the UI controls with the current tuner state. + */ + protected abstract void tunerStatusUpdated(); + + /** + * Implements the tuner status listener interface to send notification to editor implementations that the + * status of the discovered tuner has changed. + * @param discoveredTuner that has a status change. + * @param previous tuner status + * @param current tuner status + */ + @Override + public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current) + { + SwingUtils.run(() -> tunerStatusUpdated()); + + if(current == TunerStatus.ENABLED) + { + if(hasTuner()) + { + getTuner().addTunerEventListener(this); + } + } + } + + @Override + public void receive(TunerEvent tunerEvent) + { + if(tunerEvent.getEvent().equals(TunerEvent.Event.UPDATE_MEASURED_FREQUENCY_ERROR)) + { + SwingUtils.run(() -> getFrequencyPanel().updateFrequencyError()); + } + } + + /** + * Prepare this editor for disposal + */ + public void dispose() + { + if(mDiscoveredTuner != null) + { + mDiscoveredTuner.removeTunerStatusListener(this); + + if(mDiscoveredTuner.hasTuner()) + { + mDiscoveredTuner.getTuner().removeTunerEventListener(this); + } + } + + turnOffRecorder(); + } + + /** + * Indicates if the discovered tuner has a usable tuner + */ + public boolean hasTuner() + { + return mDiscoveredTuner != null && mDiscoveredTuner.hasTuner(); + } + + /** + * Access the usable tuner from the discovered tuner + * Note: use hasTuner() to check before invoking this method. + * @return tuner or null. + */ + public T getTuner() + { + if(hasTuner()) + { + return (T)mDiscoveredTuner.getTuner(); + } + + return null; + } + + /** + * Request to save the state of this configuration. + */ + protected void saveConfiguration() + { + mTunerManager.saveConfigurations(); + } + + /** + * Sets the lock state for the tuner so that the frequency and sample rate controls can be enabled/disabled. + * + * @param locked true if the tuner is locked. + */ + public void setTunerLockState(boolean locked) + { + getFrequencyPanel().updateControls(); + } + + /** + * Tuner buttons panel + */ + public class ButtonPanel extends JPanel + { + /** + * Constructs an instance + */ + public ButtonPanel() + { + setLayout(new MigLayout("insets 0,fill", "[][][][][grow,fill]", "")); + add(getChangeStatusButton()); + add(getRecordButton()); + add(getViewSpectrumButton()); + add(getNewSpectrumButton(), "wrap"); +// add(new JLabel(""), "wrap"); //Spacer to allow recording status label to grow and not affect buttons + add(getRecordingStatusLabel(), "span"); + } + + /** + * Updates the state and text of the buttons based on the tuner status. + */ + public void updateControls() + { + TunerStatus tunerStatus = getDiscoveredTuner().getTunerStatus(); + + getRecordButton().setEnabled(tunerStatus.isAvailable() && getDiscoveredTuner().hasTuner()); + getRecordingStatusLabel().setText(" "); + getViewSpectrumButton().setEnabled(tunerStatus.isAvailable() && getDiscoveredTuner().hasTuner()); + getNewSpectrumButton().setEnabled(tunerStatus.isAvailable() && getDiscoveredTuner().hasTuner()); + + switch(tunerStatus) + { + case ENABLED: + getChangeStatusButton().setText(BUTTON_STATUS_DISABLE); + break; + case DISABLED: + getChangeStatusButton().setText(BUTTON_STATUS_ENABLE); + break; + case ERROR: + getChangeStatusButton().setText(BUTTON_STATUS_RESET); + break; + } + } + } + + /** + * Sub panel that displays frequency control, ppm spinner and control, and tuner locked status label. + */ + public class FrequencyPanel extends JPanel + { + public FrequencyPanel() + { + setLayout(new MigLayout("insets 0,fill", "[][][][][grow,fill]", "")); + add(getFrequencyControl(), "spany 2"); + add(new JLabel("PPM:")); + add(getFrequencyCorrectionSpinner()); + add(getMeasuredPPMLabel(), "wrap"); + add(getAutoPPMCheckBox(), "span"); + add(getTunerLockedStatusLabel(), "span"); + } + + /** + * Update the state of the frequency panel controls + */ + public void updateControls() + { + getFrequencyControl().clearListeners(); + getFrequencyControl().addListener(mFrequencyAndCorrectionChangeListener); + getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked()); + getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLocked()); + getFrequencyCorrectionSpinner().setEnabled(hasTuner()); + getAutoPPMCheckBox().setEnabled(hasTuner()); + + Tuner tuner = getTuner(); + + if(tuner != null) + { + getFrequencyControl().setFrequency(tuner.getTunerController().getFrequency(), false); + getFrequencyCorrectionSpinner().setValue(tuner.getTunerController().getFrequencyCorrection()); + getAutoPPMCheckBox().setSelected(tuner.getTunerController().getFrequencyErrorCorrectionManager().isEnabled()); + getFrequencyControl().addListener(getTuner().getTunerController()); + getTuner().getTunerController().addListener(getFrequencyControl()); + getMeasuredPPMLabel().setText(tuner.getTunerController().getMeasuredErrorStatus()); + } + else + { + getFrequencyControl().setFrequency(0, false); + getFrequencyCorrectionSpinner().setValue(0); + getAutoPPMCheckBox().setSelected(false); + getMeasuredPPMLabel().setText(""); + } + } + + /** + * Updates the measured frequency error label + */ + public void updateFrequencyError() + { + SwingUtils.run(() -> + { + if(hasTuner()) + { + getMeasuredPPMLabel().setText(getTuner().getTunerController().getMeasuredErrorStatus()); + } + else + { + getMeasuredPPMLabel().setText(""); + } + }); + } + } + + /** + * Implements status listener to receive updates for wide-band recordings + */ + public class RecordingStatusListener implements IRecordingStatusListener + { + private DecimalFormat mSizeFormat = new DecimalFormat("0.0"); + + @Override + public void update(int fileCount, String file, long size) + { + StringBuilder sb = new StringBuilder(); + sb.append("Recording Size: ").append(humanReadableByteCount(size)); + sb.append(" File #").append(fileCount).append(": ").append(file); + final String status = sb.toString(); + EventQueue.invokeLater(() -> getRecordingStatusLabel().setText(status)); + } + + public static String humanReadableByteCount(long bytes) { + long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + if (absB < 1024) { + return bytes + " B"; + } + long value = absB; + CharacterIterator ci = new StringCharacterIterator("KMGTPE"); + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + value *= Long.signum(bytes); + return String.format("%.1f %cB", value / 1024.0, ci.current()); + } + } + + /** + * Monitors the frequency correction spinner for changed value. + */ + private class FrequencyAndCorrectionChangeListener implements ChangeListener, ISourceEventProcessor + { + //This monitors the frequency correction spinner, applies the changes to the tuner, and saves configuration. + @Override + public void stateChanged(ChangeEvent e) + { + final double value = ((SpinnerNumberModel) getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue(); + + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getTunerController().setFrequencyCorrection(value); + } + catch(SourceException e1) + { + mLog.error("Error setting frequency correction value", e1); + } + + save(); + } + } + + //This monitors the frequency control and saves configuration. + @Override + public void process(SourceEvent event) throws SourceException + { + if(hasTuner() && !isLoading()) + { + save(); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerSpectralDisplayManager.java b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerSpectralDisplayManager.java new file mode 100644 index 00000000..b90b373c --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerSpectralDisplayManager.java @@ -0,0 +1,102 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.ui; + +import io.github.dsheirer.playlist.PlaylistManager; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.settings.SettingsManager; +import io.github.dsheirer.source.tuner.Tuner; +import io.github.dsheirer.source.tuner.TunerEvent; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.spectrum.SpectralDisplayPanel; +import io.github.dsheirer.spectrum.SpectrumFrame; +import io.github.dsheirer.util.SwingUtils; +import io.github.dsheirer.util.ThreadPool; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class TunerSpectralDisplayManager implements Listener +{ + private SpectralDisplayPanel mSpectralDisplayPanel; + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; + private DiscoveredTunerModel mDiscoveredTunerModel; + + public TunerSpectralDisplayManager(SpectralDisplayPanel panel, PlaylistManager playlistManager, + SettingsManager settingsManager, DiscoveredTunerModel discoveredTunerModel) + { + mSpectralDisplayPanel = panel; + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; + mDiscoveredTunerModel = discoveredTunerModel; + } + + /** + * Shows the first available tuner from the discovered tuner model + */ + public Tuner showFirstTuner() + { + List availableTuners = mDiscoveredTunerModel.getAvailableTuners(); + + for(DiscoveredTuner discoveredTuner: availableTuners) + { + if(discoveredTuner.hasTuner()) + { + mSpectralDisplayPanel.showTuner(discoveredTuner.getTuner()); + return discoveredTuner.getTuner(); + } + } + + return null; + } + + @Override + public void receive(TunerEvent event) + { + switch(event.getEvent()) + { + case REQUEST_CLEAR_MAIN_SPECTRAL_DISPLAY: + SwingUtils.run(() -> mSpectralDisplayPanel.clearTuner()); + break; + case REQUEST_MAIN_SPECTRAL_DISPLAY: + SwingUtils.run(() -> mSpectralDisplayPanel.showTuner(event.getTuner())); + break; + case REQUEST_NEW_SPECTRAL_DISPLAY: + final SpectrumFrame frame = new SpectrumFrame(mPlaylistManager, mSettingsManager, mDiscoveredTunerModel, event.getTuner()); + SwingUtils.run(() -> frame.setVisible(true)); + break; + case NOTIFICATION_ERROR_STATE: + case NOTIFICATION_SHUTTING_DOWN: + if(event.getTuner().equals(mSpectralDisplayPanel.getTuner())) + { + SwingUtils.run(() -> + { + mSpectralDisplayPanel.clearTuner(); + ThreadPool.SCHEDULED.schedule(() -> SwingUtils.run(() -> + { + showFirstTuner(); + }), 1, TimeUnit.SECONDS); + }); + } + default: + break; + } + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java new file mode 100644 index 00000000..9242b968 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java @@ -0,0 +1,210 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.ui; + +import com.jidesoft.swing.JideSplitPane; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.manager.TunerManager; +import io.github.dsheirer.source.tuner.manager.TunerStatus; +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.RowSorter; +import javax.swing.SortOrder; +import javax.swing.event.TableModelEvent; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableRowSorter; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.List; + +/** + * Panel containing a discovered tuners table and a tuner editor for a selected tuner. + */ +public class TunerViewPanel extends JPanel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger(TunerViewPanel.class); + private static final String TABLE_PREFERENCE_KEY = "tuner.view.panel"; + + private DiscoveredTunerModel mDiscoveredTunerModel; + private JTable mTunerTable; + private JTableColumnWidthMonitor mColumnWidthMonitor; + private TableRowSorter mRowSorter; + private JideSplitPane mSplitPane; + private DiscoveredTunerEditor mDiscoveredTunerEditor; + private UserPreferences mUserPreferences; + + /** + * Constructs an instance + * @param tunerManager for tuners + * @param userPreferences for making recordings in the tuner editor + */ + public TunerViewPanel(TunerManager tunerManager, UserPreferences userPreferences) + { + mDiscoveredTunerModel = tunerManager.getDiscoveredTunerModel(); + mDiscoveredTunerEditor = new DiscoveredTunerEditor(userPreferences, tunerManager); + mUserPreferences = userPreferences; + init(); + } + + private void init() + { + setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow]", "[fill,grow]")); + + mRowSorter = new TableRowSorter<>(mDiscoveredTunerModel); + List sortKeys = new ArrayList<>(); + sortKeys.add(new RowSorter.SortKey(DiscoveredTunerModel.COLUMN_TUNER_TYPE, SortOrder.ASCENDING)); + mRowSorter.setSortKeys(sortKeys); + + mTunerTable = new JTable(mDiscoveredTunerModel); + mTunerTable.setRowSorter(mRowSorter); + mTunerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + mTunerTable.getSelectionModel().addListSelectionListener(event -> + { + if(!event.getValueIsAdjusting()) + { + int row = mTunerTable.getSelectedRow(); + + if(row >= 0) + { + int modelRow = mTunerTable.convertRowIndexToModel(row); + mDiscoveredTunerEditor.setItem(mDiscoveredTunerModel.getDiscoveredTuner(modelRow)); + } + } + }); + + //Monitor for tuner removal events so we can update the editor when our selected tuner is removed + mDiscoveredTunerModel.addTableModelListener(e -> + { + //Detect when status is for the currently selected tuner + if(e.getType() == TableModelEvent.DELETE && + mDiscoveredTunerEditor.hasItem() && + !mDiscoveredTunerModel.hasTuner(mDiscoveredTunerEditor.getItem())) + { + mDiscoveredTunerEditor.setItem(null); + } + }); + + mDiscoveredTunerModel.addListener(tunerEvent -> + { + switch(tunerEvent.getEvent()) + { + case UPDATE_LOCK_STATE: + if(tunerEvent.getTuner() != null) + { + int row = mTunerTable.getSelectedRow(); + + if(row >= 0) + { + int modelRow = mTunerTable.convertRowIndexToModel(row); + DiscoveredTuner selectedTuner = mDiscoveredTunerModel.getDiscoveredTuner(modelRow); + + if(selectedTuner != null && selectedTuner.hasTuner() && tunerEvent.getTuner() == selectedTuner.getTuner()) + { + mDiscoveredTunerEditor.setTunerLockState(selectedTuner.getTuner().getTunerController().isLocked()); + } + } + } + break; + } + }); + + + TableCellRenderer errorCellRenderer = new TunerStatusCellRenderer(); + mTunerTable.getColumnModel().getColumn(DiscoveredTunerModel.COLUMN_TUNER_STATUS).setCellRenderer(errorCellRenderer); + + mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTunerTable, TABLE_PREFERENCE_KEY); + JScrollPane tunerTableScroller = new JScrollPane(mTunerTable); + + JPanel tunerTablePanel = new JPanel(); + tunerTablePanel.setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow][]", "[fill,grow][]")); + tunerTablePanel.add(tunerTableScroller, "span"); + + tunerTablePanel.add(new JLabel("")); //Empty spacer + +// JButton addRecordingTunerButton = new JButton("Add Recording Tuner"); +// addRecordingTunerButton.addActionListener(e -> +// { +// AddRecordingTunerDialog dialog = new AddRecordingTunerDialog(mUserPreferences, mDiscoveredTunerModel); +// dialog.setLocationRelativeTo(TunerViewPanel.this); +// EventQueue.invokeLater(() -> dialog.setVisible(true)); +// }); +// +// tunerTablePanel.add(addRecordingTunerButton); + + tunerTablePanel.setPreferredSize(new Dimension(200,200)); + JScrollPane editorScroller = new JScrollPane(mDiscoveredTunerEditor); + editorScroller.setPreferredSize(new Dimension(200, 200)); + + mSplitPane = new JideSplitPane(); + mSplitPane.setOrientation(JideSplitPane.HORIZONTAL_SPLIT); + mSplitPane.add(tunerTablePanel); + mSplitPane.add(editorScroller); + + add(mSplitPane); + } + + /** + * Custom cell renderer for the TunerStatus enumeration column + */ + public class TunerStatusCellRenderer extends DefaultTableCellRenderer + { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) + { + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if(value instanceof TunerStatus tunerStatus) + { + if(tunerStatus == TunerStatus.ERROR) + { + component.setForeground(Color.RED); + } + else if(tunerStatus == TunerStatus.DISABLED) + { + component.setForeground(Color.DARK_GRAY); + } + else + { + component.setForeground(table.getForeground()); + } + } + else + { + component.setForeground(table.getForeground()); + } + + return component; + } + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java deleted file mode 100644 index 067e15d4..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ -package io.github.dsheirer.source.tuner.usb; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsb; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Scheduled runnable for the primary LibUsb processing thread. Each USB device's transfer processor - * registers with this processor to support auto-start/stop of this event processing thread. - */ -public class USBMasterProcessor -{ - private final static Logger mLog = LoggerFactory.getLogger(USBMasterProcessor.class); - - private static final long USB_TIMEOUT_MS = 2000l; //milliseconds - - private List mRegisteredProcessors = new CopyOnWriteArrayList<>(); - private AtomicBoolean mRunning = new AtomicBoolean(); - private Processor mProcessor = new Processor(); - private Thread mProcessorThread; - - /** - * Registers the transfer processor so that LibUSB timeout processing will auto-start. - * @param processor to register - */ - public void registerTransferProcessor(USBTransferProcessor processor) - { - if(!mRegisteredProcessors.contains(processor)) - { - mRegisteredProcessors.add(processor); - } - - start(); - } - - /** - * Unregisters the transfer processor so that LibUSB timeout processing will auto-stop once all processors have - * been unregistered. - * - * @param processor to unregister - */ - public void unregisterTransferProcessor(USBTransferProcessor processor) - { - mRegisteredProcessors.remove(processor); - - if(mRegisteredProcessors.isEmpty()) - { - stop(); - } - } - - /** - * Starts processing of LibUSB timeout events - */ - private void start() - { - if(mRunning.compareAndSet(false, true)) - { - mLog.info("Starting USB master processor thread"); - if(mProcessorThread == null) - { - mProcessor.reset(); - mProcessorThread = new Thread(mProcessor); - mProcessorThread.setName("sdrtrunk USB event processor"); - mProcessorThread.setPriority(Thread.MAX_PRIORITY); - mProcessorThread.start(); - } - } - } - - /** - * Stops processing of LibUSB timeout events - */ - private void stop() - { - if(mRunning.compareAndSet(true, false)) - { - mLog.info("Stopping USB master processor thread"); - if(mProcessorThread != null) - { - mProcessor.stop(); - mProcessorThread.interrupt(); - - try - { - mProcessorThread.join(1000); - } - catch(InterruptedException ie) - { - //No action - } - - mProcessorThread = null; - } - } - } - - /** - * Stops the libusb timeout processor and prepares for shutdown. - */ - public void shutdown() - { - stop(); - } - - /** - * Runnable to invoke LibUSB timeout handler. All downstream completed transfer processing will occur on this - * thread instance. - */ - class Processor implements Runnable - { - private boolean mRunning = true; - - /** - * Sets stop flag to signal to stop processing - */ - public void stop() - { - mRunning = false; - } - - /** - * Resets this processor so that it can be reused. - */ - public void reset() - { - mRunning = true; - } - - @Override - public void run() - { - while(mRunning) - { - try - { - LibUsb.handleEvents(null); - } - catch(Throwable throwable) - { - mLog.error("Error while processing LibUSB timeout events", throwable); - } - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java deleted file mode 100644 index 972ab226..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java +++ /dev/null @@ -1,587 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ -package io.github.dsheirer.source.tuner.usb; - -import io.github.dsheirer.buffer.INativeBuffer; -import io.github.dsheirer.buffer.INativeBufferFactory; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.tuner.ITunerErrorListener; -import io.github.dsheirer.source.tuner.TunerManager; -import io.github.dsheirer.util.ThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import org.usb4java.Transfer; -import org.usb4java.TransferCallback; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -public class USBTransferProcessor implements TransferCallback -{ - private final static Logger mLog = LoggerFactory.getLogger(USBTransferProcessor.class); - - private static final byte USB_BULK_TRANSFER_ENDPOINT = (byte) 0x81; - private static final long USB_TIMEOUT_MS = 2000l; //milliseconds - - //Number of native byte buffers to allocate for transferring data from the USB device - private static final int TRANSFER_BUFFER_POOL_SIZE = 40; - - private LinkedTransferQueue mAvailableTransfers = new LinkedTransferQueue<>(); - private LinkedTransferQueue mInProgressTransfers = new LinkedTransferQueue<>(); - private LinkedTransferQueue mCompletedTransfers = new LinkedTransferQueue<>(); - private List mTransfersToDispose = new ArrayList<>(); - private List mTransfersToSubmit = new ArrayList<>(); - - //Tuner specific native buffer factory for processing/conversion of raw samples - private INativeBufferFactory mNativeBufferFactory; - - //Byte array transfer buffers size in bytes - private int mBufferSize; - - private Listener mNativeBufferListener; - - //Handle to the USB bulk transfer device - private DeviceHandle mUsbBulkTransferDeviceHandle; - private AtomicBoolean mRunning = new AtomicBoolean(); - private AtomicBoolean mRestarting = new AtomicBoolean(); - private CompletedTransferProcessor mCompletedTransferProcessor = new CompletedTransferProcessor(); - private ScheduledFuture mBufferDispatcherFuture; - private String mDeviceName; - private int mTransferErrorLoggingCount = 0; - private int mRestartCount = 0; - private ITunerErrorListener mITunerErrorListener; - - /** - * Manages stream of USB transfer buffers and converts buffers to complex buffer samples for distribution to - * any registered listeners. - * - * @param deviceName to use when logging information or errors - * @param usbBulkTransferDeviceHandle to the USB bulk transfer device - * @param nativeBufferFactory specific to the tuner's byte buffer format for converting to floating point I/Q samples - * @param bufferSize in bytes. Should be a multiple of two: 65536, 131072 or 262144. - */ - public USBTransferProcessor(String deviceName, DeviceHandle usbBulkTransferDeviceHandle, - INativeBufferFactory nativeBufferFactory, int bufferSize, - ITunerErrorListener tunerErrorListener) - { - mDeviceName = deviceName; - mUsbBulkTransferDeviceHandle = usbBulkTransferDeviceHandle; - mNativeBufferFactory = nativeBufferFactory; - mBufferSize = bufferSize; - mITunerErrorListener = tunerErrorListener; - } - - /** - * Modifies the usb transfer buffer size used for transfering native byte buffers from the - * USB device. Note: changing the buffer size while the transfer processor is running causes - * the transfer to stop momentarily while the existing buffers are destroyed and new buffers - * with the correct size are recreated. - * - * Note: this method is not thread safe. Ensure that no other threads invoke stop() or start() - * while a buffer size change is in progress. - * - * @param bufferSize to use for native usb buffer transfers - */ - public void setBufferSize(int bufferSize) - { - if(bufferSize % 2 == 1) - { - throw new IllegalArgumentException("Buffer size must be a multiple of 2 for complex samples"); - } - - if(mBufferSize != bufferSize) - { - stop(); - mBufferSize = bufferSize; - start(); - } - } - - /** - * Start USB transfer buffer processing. Subsequent calls to this method after started will be ignored. - * - * @return true if start successful with no errors or is already running. returns false if errors on startup. - */ - private boolean start() - { - boolean success = false; - - if(mRunning.compareAndSet(false, true)) - { - prepareDeviceStart(); - prepareTransfers(); - - if(submitTransfers()) - { - success = true; - //Start transferred buffer dispatcher - mBufferDispatcherFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mCompletedTransferProcessor, - 0, 6, TimeUnit.MILLISECONDS); - - //Register with LibUSB processor so that it auto-starts LibUSB processing - TunerManager.LIBUSB_TRANSFER_PROCESSOR.registerTransferProcessor(this); - } - } - - return success; - } - - /** - * Stop USB transfer buffer processing. Subsequent calls to this method after stopped will be ignored. - */ - private void stop() - { - if(mRunning.compareAndSet(true, false)) - { - if(mBufferDispatcherFuture != null) - { - mBufferDispatcherFuture.cancel(false); - mBufferDispatcherFuture = null; - } - - //Cancel all buffers that are currently in progress - for(Transfer transfer : mInProgressTransfers) - { - LibUsb.cancelTransfer(transfer); - } - - //Await completion of all in-progress transfers - int waitCycleCount = 0; - while(!mInProgressTransfers.isEmpty() && waitCycleCount < 30) - { - waitCycleCount++; - - try - { - Thread.sleep(10); - } - catch(InterruptedException ie) - { - //No - action - } - } - - //Clear all completed buffers - Transfer completedTransfer = mCompletedTransfers.poll(); - - while(completedTransfer != null) - { - mAvailableTransfers.add(completedTransfer); - completedTransfer = mCompletedTransfers.poll(); - } - - //Unregister from LibUSB processor so that it auto-stops LibUSB event timeout processing - TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(this); - - executeDeviceStop(); - - disposeTransfers(); - } - } - - /** - * Restarts the device after there is an error. Initially stops the device and then schedules a start() to - * occur in 10 milliseconds. - */ - private void restart() - { - if(mRestarting.compareAndSet(false, true)) - { - stop(); - - if(mRestartCount >= 3) - { - mLog.warn("[" + mDeviceName + "] - maximum restart attempts [" + mRestartCount + - "] exceeded for tuner - stopping"); - - if(mITunerErrorListener != null) - { - mITunerErrorListener.setErrorMessage("USB ERROR"); - } - - return; - } - - mRestartCount++; - - //Attempt to clear any halt condition - LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); - - mLog.info("[" + mDeviceName + "] - restart attempt [" + mRestartCount + "]"); - - Runnable runnable = () -> - { - mRestarting.set(false); - - boolean success = start(); - - if(!success) - { - mLog.warn("[" + mDeviceName + "] - restart attempt [" + mRestartCount + "] failed - retrying"); - restart(); - } - }; - - ThreadPool.SCHEDULED.schedule(runnable, 20, TimeUnit.MILLISECONDS); - } - } - - /** - * (Re)Submits transfer buffers to the USB device. Since we're offloading byte buffer to complex sample conversion - * to another thread, there may be times when sufficient transfer buffers are not available to keep a level number - * of transfer buffers in progress. Therefore, we track the transfer buffers to submit deficit number and attempt - * to play catch-up each time this method is invoked. - * - * @return boolean true if there were no errors submitting transfer buffers. A false value indicates that there - * were errors and that the device likely needs to be reset. - */ - private boolean submitTransfers() - { - if(mRunning.get()) - { - mAvailableTransfers.drainTo(mTransfersToSubmit); - - if(!mTransfersToSubmit.isEmpty()) - { - Transfer transfer = mTransfersToSubmit.remove(0); - - while(transfer != null) - { - int status = LibUsb.submitTransfer(transfer); - - switch(status) - { - case LibUsb.SUCCESS: - mInProgressTransfers.add(transfer); - - if(!mTransfersToSubmit.isEmpty()) - { - transfer = mTransfersToSubmit.remove(0); - } - else - { - transfer = null; - } - break; - case LibUsb.ERROR_PIPE: - mLog.warn("[" + mDeviceName + "] - USB Pipe Error while submitting transfer buffers"); - LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); - mTransfersToDispose.add(transfer); - transfer = null; - break; - default: - mLog.error("[" + mDeviceName + "] - " + getErrorStatus(status) + " while submitting transfer buffers"); - mTransfersToDispose.add(transfer); - transfer = null; - break; - } - } - } - - //If we aborted submitting transfers or if we had an error while submitting, move the remaining transfers - // back to the available queue and restart the device - if(!mTransfersToSubmit.isEmpty()) - { - mAvailableTransfers.addAll(mTransfersToSubmit); - mTransfersToSubmit.clear();; - } - } - - return mTransfersToDispose.isEmpty(); - } - - /** - * Frees all allocated transfers in preparation for shutdown - */ - private void disposeTransfers() - { - mAvailableTransfers.drainTo(mTransfersToDispose); - - if(mTransfersToDispose.size() != TRANSFER_BUFFER_POOL_SIZE) - { - mLog.warn("[" + mDeviceName + "] - potential memory leak [" + - mTransfersToDispose.size() + " of " + TRANSFER_BUFFER_POOL_SIZE + - "] USB transfer buffers will be disposed during shutdown"); - } - - for(Transfer transfer: mTransfersToDispose) - { - LibUsb.freeTransfer(transfer); - } - - mTransfersToDispose.clear(); - } - - /** - * Allows sub-class implementations to execute any device-specific operations to prepare for starting USB transfers - */ - protected void prepareDeviceStart() - { - - } - - /** - * Allows sub-class implementations to execute any device-specific operations after stopping USB transfers - */ - protected void executeDeviceStop() - { - - } - - /** - * Sets the listener and auto-starts the buffer processor - */ - public void setListener(Listener listener) - { - if(mNativeBufferListener == null || !mNativeBufferListener.equals(listener)) - { - mNativeBufferListener = listener; - - boolean success = start(); - - if(!success) - { - restart(); - } - } - } - - /** - * Auto-stops the buffer processor and removes the listener - */ - public void removeListener() - { - if(mNativeBufferListener != null) - { - stop(); - mNativeBufferListener = null; - } - } - - /** - * Prepares (allocates) a set of transfer buffers for use in transferring data from the USB device via the bulk - * interface. Since we're using direct memory allocation (native), buffers are retained and reused across multiple - * start/stop cycles. - */ - private void prepareTransfers() throws LibUsbException - { - while(mAvailableTransfers.size() < TRANSFER_BUFFER_POOL_SIZE) - { - Transfer transfer = LibUsb.allocTransfer(); - - if(transfer == null) - { - throw new LibUsbException("Couldn't allocate USB transfer buffer", LibUsb.ERROR_NO_MEM); - } - - final ByteBuffer buffer = ByteBuffer.allocateDirect(mBufferSize); - - LibUsb.fillBulkTransfer(transfer, mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT, buffer, this, - "Buffer", USB_TIMEOUT_MS); - - mAvailableTransfers.add(transfer); - } - } - - /** - * Process a filled transfer buffer received back from the USB device. Note: this method is invoked on the USB - * bus processing thread, so we try to keep processing to a minimum and place transfers in the completed - * transfer queue so that the scheduled processor thread handles any conversion and additional downstream - * processing workload. - */ - @Override - public void processTransfer(Transfer transfer) - { - mInProgressTransfers.remove(transfer); - - switch(transfer.status()) - { - case LibUsb.TRANSFER_COMPLETED: - case LibUsb.TRANSFER_STALL: - case LibUsb.TRANSFER_TIMED_OUT: - if(transfer.actualLength() > 0) - { - mCompletedTransfers.add(transfer); - } - else - { - transfer.buffer().rewind(); - mAvailableTransfers.add(transfer); - } - break; - case LibUsb.TRANSFER_ERROR: - if(transfer.actualLength() > 0) - { - mCompletedTransfers.add(transfer); - } - else - { - transfer.buffer().rewind(); - mAvailableTransfers.add(transfer); - - mTransferErrorLoggingCount++; - - if(mTransferErrorLoggingCount <= 5) - { - //Handle integer overflow - if(mTransferErrorLoggingCount < 0) - { - mTransferErrorLoggingCount = 1; - } - } - } - - break; - case LibUsb.TRANSFER_CANCELLED: - transfer.buffer().rewind(); - mAvailableTransfers.add(transfer); - break; - default: - //Unexpected transfer error - need to reset the bulk transfer interface - mLog.error("[" + mDeviceName + "] - transfer error [" + getTransferStatus(transfer.status()) + - "] transferred actual: " + transfer.actualLength()); - transfer.buffer().rewind(); - mAvailableTransfers.add(transfer); - restart(); - return; - } - } - - /** - * Converts the error status code to a textual description - */ - public static String getErrorStatus(int status) - { - switch(status) - { - case LibUsb.SUCCESS: - return "SUCCESS"; - case LibUsb.ERROR_IO: - return "USB I/O ERROR"; - case LibUsb.ERROR_INVALID_PARAM: - return "USB INVALID PARAMETERS ERROR"; - case LibUsb.ERROR_ACCESS: - return "USB ACCESS ERROR"; - case LibUsb.ERROR_NO_DEVICE: - return "USB NO DEVICE ERROR"; - case LibUsb.ERROR_NOT_FOUND: - return "USB NOT FOUND ERROR"; - case LibUsb.ERROR_BUSY: - return "USB BUSY ERROR"; - case LibUsb.ERROR_TIMEOUT: - return "USB TIMEOUT ERROR"; - case LibUsb.ERROR_OVERFLOW: - return "USB OVERFLOW ERROR"; - case LibUsb.ERROR_PIPE: - return "USB PIPE ERROR"; - case LibUsb.ERROR_INTERRUPTED: - return "USB INTERRUPTED ERROR"; - case LibUsb.ERROR_NO_MEM: - return "USB NO MEMORY ERROR"; - case LibUsb.ERROR_NOT_SUPPORTED: - return "USB NOT SUPPORTED ERROR"; - default: - return "USB UNKNOWN [" + status + "] ERROR"; - } - } - - /** - * Converts the USB transfer status number into a descriptive label - */ - public static String getTransferStatus(int status) - { - switch(status) - { - case 0: - return "TRANSFER COMPLETED (0)"; - case 1: - return "TRANSFER ERROR (1)"; - case 2: - return "TRANSFER TIMED OUT (2)"; - case 3: - return "TRANSFER CANCELLED (3)"; - case 4: - return "TRANSFER STALL (4)"; - case 5: - return "TRANSFER NO DEVICE (5)"; - case 6: - return "TRANSFER OVERFLOW (6)"; - default: - return "UNKNOWN TRANSFER STATUS (" + status + ")"; - } - } - - /** - * Processes completed USB buffer transfers, converts the transferred bytes into complex samples and dispatches the - * sample buffer to the listener. - */ - public class CompletedTransferProcessor implements Runnable - { - @Override - public void run() - { - try - { - Transfer transfer = mCompletedTransfers.poll(); - - while(transfer != null) - { - if(mRunning.get()) - { - if(mNativeBufferListener != null) - { - //Pass the transfer's byte buffer so the native buffer factory can make a copy of the - //byte array contents and package it as a native buffer. - INativeBuffer nativeBuffer = mNativeBufferFactory.getBuffer(transfer.buffer(), - System.currentTimeMillis()); - mNativeBufferListener.receive(nativeBuffer); - } - } - - transfer.buffer().rewind(); - mAvailableTransfers.add(transfer); - - boolean success = submitTransfers(); - - if(success) - { - transfer = mCompletedTransfers.poll(); - } - else - { - ThreadPool.CACHED.submit(() -> restart()); - transfer = null; - } - } - } - catch(Throwable throwable) - { - mLog.error("[" + mDeviceName + "] - error while processing USB transfer buffers", throwable); - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java index ccf2e8af..d2239d7b 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java @@ -19,54 +19,679 @@ package io.github.dsheirer.source.tuner.usb; import io.github.dsheirer.buffer.INativeBuffer; +import io.github.dsheirer.buffer.INativeBufferFactory; import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.TunerController; +import io.github.dsheirer.source.tuner.TunerType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.Context; +import org.usb4java.Device; +import org.usb4java.DeviceDescriptor; +import org.usb4java.DeviceHandle; +import org.usb4java.DeviceList; +import org.usb4java.LibUsb; +import org.usb4java.Transfer; +import org.usb4java.TransferCallback; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Tuner controller implementation for USB tuners. Manages general USB operations and incorporates threaded USB + * Transfer processing. + */ public abstract class USBTunerController extends TunerController { + private Logger mLog = LoggerFactory.getLogger(USBTunerController.class); + private static final int USB_INTERFACE = 0x0; //Common value for all currently supported devices + private static final int USB_CONFIGURATION = 0x1; //Common value for all currently supported devices + private static final int USB_BULK_TRANSFER_BUFFER_POOL_SIZE = 8; + private static final byte USB_BULK_TRANSFER_ENDPOINT = (byte) 0x81; + private static final long USB_BULK_TRANSFER_TIMEOUT_MS = 2000l; + + private int mBus; + private int mPort; + private Context mDeviceContext = new Context(); + private Device mDevice; + private DeviceHandle mDeviceHandle; + private DeviceDescriptor mDeviceDescriptor; + private TransferManager mTransferManager = new TransferManager(); + private UsbEventProcessor mEventProcessor = new UsbEventProcessor(); + private AtomicBoolean mStreaming = new AtomicBoolean(); + private ReentrantLock mListenerLock = new ReentrantLock(); + private boolean mRunning = false; + /** * USB tuner controller class. Provides auto-start and auto-stop function when complex buffer listeners are added * or removed from this tuner controller. * - * @param minimumFrequency minimum uncorrected tunable frequency - * @param maximumFrequency maximum uncorrected tunable frequency - * @param middleUnusable is the +/- value center DC spike to avoid for channels - * @param usableBandwidth percentage of usable bandwidth relative to space at the extreme ends of the spectrum + * @param bus number USB + * @param port number USB + * @param tunerErrorListener to receive errors from this tuner controller */ - public USBTunerController(long minimumFrequency, long maximumFrequency, int middleUnusable, double usableBandwidth) + public USBTunerController(int bus, int port, ITunerErrorListener tunerErrorListener) { - super(minimumFrequency, maximumFrequency, middleUnusable, usableBandwidth); + super(tunerErrorListener); + mBus = bus; + mPort = port; } - protected abstract USBTransferProcessor getUSBTransferProcessor(); + /** + * Constructs an instance + * @param bus usb + * @param port usb + * @param minimum tunable frequency in Hertz + * @param maximum tunable frequency in Hertz + * @param halfBandwidth that is unusable for DC spike avoidance + * @param usablePercent bandwith in Hertz + * @param tunerErrorListener to receive errors from this tuner controller + */ + public USBTunerController(int bus, int port, long minimum, long maximum, int halfBandwidth, double usablePercent, + ITunerErrorListener tunerErrorListener) + { + this(bus, port, tunerErrorListener); + setMinimumFrequency(minimum); + setMaximumFrequency(maximum); + setMiddleUnusableHalfBandwidth(halfBandwidth); + setUsableBandwidthPercentage(usablePercent); + } /** - * Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started. + * Tuner type for this USB controller + */ + public abstract TunerType getTunerType(); + + /** + * Factory for converting received streaming sample data into native buffers, as provided by sub-class. + */ + protected abstract INativeBufferFactory getNativeBufferFactory(); + + /** + * Sub-class definition of transfer buffer sizes to use for the tuner. Note: this should be a power-of-two + * value for compatibility with downstream (SIMD) operations (e.g. 65,536, 131072, 262144, etc.) + * @return transfer buffer size. + */ + protected abstract int getTransferBufferSize(); + + /** + * Sub-class method to perform additional device setup steps after the USB interface has been claimed and before any + * transfer operations start. + * @throws SourceException if there is an issue in configuring the device + */ + protected abstract void deviceStart() throws SourceException; + + /** + * Sub-class method to perform additional device shutdown steps after transfer processing has stopped and before + * the USB interface is released. + */ + protected abstract void deviceStop(); + + /** + * Starts or initializes this tuner. + * + * Note: sub-class implementations should override and invoke this method and then perform additional initialization + * operations for the tuner. + * + * @throws SourceException if there is an error and/or this tuner is unusable. + */ + public final void start() throws SourceException + { + if(mDeviceContext == null) + { + throw new SourceException("Device cannot be reused once it has been shutdown"); + } + + int status = LibUsb.init(mDeviceContext); + + if(status != LibUsb.SUCCESS) + { + throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status)); + } + + mDevice = findDevice(); + mDeviceDescriptor = new DeviceDescriptor(); + status = LibUsb.getDeviceDescriptor(mDevice, mDeviceDescriptor); + + if(status != LibUsb.SUCCESS) + { + mDeviceDescriptor = null; + throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status)); + } + + mDeviceHandle = new DeviceHandle(); + status = LibUsb.open(mDevice, mDeviceHandle); + + if(status == LibUsb.ERROR_ACCESS) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + + mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules"); + throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules"); + } + else if(status != LibUsb.SUCCESS) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + + mLog.error("Can't open USB tuner - check driver or Linux udev rules"); + throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status)); + } + + //Detach the kernel driver if active and detach is supported. Otherwise, let the claim interface fail. + status = LibUsb.kernelDriverActive(mDeviceHandle, USB_INTERFACE); + + if(status == 1) //kernel driver is attached and detach operation is supported + { + status = LibUsb.detachKernelDriver(mDeviceHandle, USB_INTERFACE); + + if(status != LibUsb.SUCCESS) + { + mLog.error("Unable to detach kernel driver for USB tuner device - bus:" + mBus + " port:" + mPort); + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("Can't detach kernel driver"); + } + } + + //Set the configuration which also invokes a soft reset on the device + status = LibUsb.setConfiguration(mDeviceHandle, USB_CONFIGURATION); + + if(status == LibUsb.ERROR_BUSY) + { + mLog.error("Unable to set USB configuration on tuner - device is busy (in use by another application)"); + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("USB tuner is in-use by another application"); + } + else if(status != LibUsb.SUCCESS) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("Can't set configuration (ie reset) on the USB tuner - " + LibUsb.errorName(status)); + } + + //Claim the interface + status = LibUsb.claimInterface(mDeviceHandle, USB_INTERFACE); + + if(status == LibUsb.ERROR_BUSY) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("USB tuner is in-use by another application"); + } + else if(status != LibUsb.SUCCESS) + { + mDeviceHandle = null; + mDeviceDescriptor = null; + throw new SourceException("Can't claim interface on USB tuner - " + LibUsb.errorName(status)); + } + + //Set running true for deviceStart() operations that require it. + mRunning = true; + + try + { + deviceStart(); + } + catch(Exception se) + { + mRunning = false; + throw se; + } + } + + /** + * Prepares the tuner for full shutdown by stopping streaming, shutdown the device, and releasing the USB resources. + */ + public final void stop() + { + mRunning = false; + stopStreaming(); + mNativeBufferBroadcaster.clear(); + deviceStop(); + + if(mDeviceHandle != null) + { + LibUsb.releaseInterface(mDeviceHandle, USB_INTERFACE); + LibUsb.close(mDeviceHandle); + mDeviceHandle = null; + mDevice = null; + mDeviceDescriptor = null; + } + + LibUsb.exit(mDeviceContext); + mDeviceContext = null; + } + + /** + * Starts streaming data from the tuner + */ + private void startStreaming() + { + if(mStreaming.compareAndSet(false, true)) + { + try + { + prepareStreaming(); + List transfers = mTransferManager.getTransfers(); + mEventProcessor.start(); + mTransferManager.setAutoResubmitTransfers(true); + mTransferManager.submitTransfers(transfers); + } + catch(SourceException se) + { + mLog.error("Error starting streaming on USB tuner", se); + } + } + } + + /** + * Prepares to start streaming. This method can be overridden by sub-class to implement additional actions + * need to prepare before start streaming. + */ + protected void prepareStreaming() + { + } + + /** + * Stop streaming data from the tuner + */ + private void stopStreaming() + { + if(mStreaming.compareAndSet(true, false)) + { + //Turn off auto-resubmit of USB transfer buffers + mTransferManager.setAutoResubmitTransfers(false); + + //Stop event processing thread to put all submitted tranfers in a stable state - blocks until stopped + mEventProcessor.stop(); + + //Cancel all currently submitted transfers + mTransferManager.cancelTransfers(); + + //Perform final event processing iteration so LibUsb returns all of our cancelled tranfers + mEventProcessor.handleFinalEvents(); + } + } + + /** + * Finds the USB device for this tuner at the specified USB bus and port. + * @return discovered USB device + * @throws SourceException if there is an error or the device is not discovered. + */ + private Device findDevice() throws SourceException + { + DeviceList deviceList = new DeviceList(); + int count = LibUsb.getDeviceList(mDeviceContext, deviceList); + + if(count >= 0) + { + for(Device device: deviceList) + { + int bus = LibUsb.getBusNumber(device); + int port = LibUsb.getPortNumber(device); + + if(mBus == bus && mPort == port) + { + return device; + } + } + } + + throw new SourceException("LibUsb couldn't discover USB device [" + mBus + ":" + mPort + + "] from device list" + (count < 0 ? " - error: " + LibUsb.errorName(count) : "")); + } + + /** + * Access the discovered USB device. + */ + protected Device getDevice() + { + return mDevice; + } + + /** + * LibUsb context for this device. + */ + protected Context getDeviceContext() + { + return mDeviceContext; + } + + /** + * LibUsb device descriptor for this device + */ + protected DeviceDescriptor getDeviceDescriptor() + { + return mDeviceDescriptor; + } + + /** + * USB Device Handle for the claimed device + */ + protected DeviceHandle getDeviceHandle() + { + return mDeviceHandle; + } + + /** + * Indicates if the device handle is non-null + */ + protected boolean hasDeviceHandle() + { + return getDeviceHandle() != null; + } + + /** + * Indicates if this device is usable, meaning it has been started and is not yet stopping. + * + * Note: this is a general usability flag for controlling all code that touches the USB interface(s) + */ + protected boolean isRunning() + { + return mRunning; + } + + /** + * Adds the IQ buffer listener and automatically starts stream buffer transfer processing, if not already started. */ @Override public void addBufferListener(Listener listener) { - boolean hasExistingListeners = hasBufferListeners(); - - super.addBufferListener(listener); - - if(!hasExistingListeners) + if(isRunning()) { - getUSBTransferProcessor().setListener(this); + mListenerLock.lock(); + + try + { + boolean hasExistingListeners = hasBufferListeners(); + + super.addBufferListener(listener); + + if(!hasExistingListeners) + { + startStreaming(); + } + } + finally + { + mListenerLock.unlock(); + } } } /** - * Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners. + * Removes the IQ buffer listener and stops stream buffer transfer processing if there are no more listeners. */ @Override public void removeBufferListener(Listener listener) { - super.removeBufferListener(listener); + mListenerLock.lock(); - if(!hasBufferListeners()) + try { - getUSBTransferProcessor().removeListener(); + super.removeBufferListener(listener); + + if(!hasBufferListeners()) + { + stopStreaming(); + } + } + finally + { + mListenerLock.unlock(); + } + } + + /** + * Manages USB transfer (ie zero-copy) buffer processing + */ + class TransferManager implements TransferCallback + { + private List mAvailableTransfers; + private LinkedTransferQueue mInProgressTransfers = new LinkedTransferQueue<>(); + private boolean mAutoResubmitTransfers = false; + + /** + * Creates USB Transfers to carry the streaming sample data. Transfer buffers are backed by native memory + * byte buffers outside the JVM. + * + * @return list of transfers + * @throws SourceException if there is an error creating transfers + */ + private List getTransfers() throws SourceException + { + if(mAvailableTransfers == null) + { + mAvailableTransfers = new ArrayList<>(); + + for(int x = 0; x < USB_BULK_TRANSFER_BUFFER_POOL_SIZE; x++) + { + Transfer transfer = LibUsb.allocTransfer(); + + if(transfer == null) + { + throw new SourceException("Couldn't allocate USB transfer buffer - out of memory"); + } + + final ByteBuffer buffer = ByteBuffer.allocateDirect(getTransferBufferSize()); + + LibUsb.fillBulkTransfer(transfer, mDeviceHandle, USB_BULK_TRANSFER_ENDPOINT, buffer, + TransferManager.this, "Transfer Buffer " + x, USB_BULK_TRANSFER_TIMEOUT_MS); + + mAvailableTransfers.add(transfer); + } + } + + return mAvailableTransfers; + } + + /** + * Prepare to stop processing transfers when stopping streaming of data. + */ + private void setAutoResubmitTransfers(boolean resubmit) + { + mAutoResubmitTransfers = resubmit; + } + + /** + * Submits the transfers to start sample stream processing + * @param transfers to submit + */ + private void submitTransfers(List transfers) + { + for(Transfer transfer: transfers) + { + submitTransfer(transfer); + } + } + + /** + * (Re)Submits the transfer for stream processing + * @param transfer to (re)submit + */ + private void submitTransfer(Transfer transfer) + { + int status = LibUsb.submitTransfer(transfer); + + if(status == LibUsb.SUCCESS) + { + mInProgressTransfers.add(transfer); + } + else + { + mLog.error("Error submitting USB transfer - " + LibUsb.errorName(status)); + } + } + + /** + * Cancels any in-progress transfers to prepare for shutdown. + * + * Note: this should only be invoked after the LibUsb event processing thread has been stopped so that the + * transfer buffers are in a stable (submitted vs callback) state and we can then flip their cancel state and + * then finish processing the timeout events under the control of a single (shutdown) thread. + */ + private void cancelTransfers() + { + for(Transfer transfer: mInProgressTransfers) + { + LibUsb.cancelTransfer(transfer); + } + } + + @Override + public void processTransfer(Transfer transfer) + { + mInProgressTransfers.remove(transfer); + + switch(transfer.status()) + { + case LibUsb.TRANSFER_COMPLETED: + case LibUsb.TRANSFER_STALL: + case LibUsb.TRANSFER_TIMED_OUT: + case LibUsb.TRANSFER_ERROR: + if(transfer.actualLength() > 0) + { + dispatchTransfer(transfer); + } + + transfer.buffer().rewind(); + + if(mAutoResubmitTransfers) + { + submitTransfer(transfer); + } + break; + case LibUsb.TRANSFER_CANCELLED: + //Reset the transfer but don't do anything else + transfer.buffer().rewind(); + break; + default: + //Unexpected transfer error - need to reset the bulk transfer interface + transfer.buffer().rewind(); + //TODO: usually the device is in a bad state at this point ... need to raise the error flag + return; + } + } + + /** + * Disposes of the transfer and dereferences the native byte buffer so that it can be garbage collected. + */ + private void disposeTransfers() + { + for(Transfer transfer: mAvailableTransfers) + { + transfer.setBuffer(null); + } + + mAvailableTransfers.clear(); + mAvailableTransfers = null; + } + + /** + * Makes a copy of the transfer's native memory byte array payload so that the transfer can be reused. + * Dispatches the native buffer to registered listeners. + * @param transfer to copy and dispatch + */ + private void dispatchTransfer(Transfer transfer) + { + //Pass the transfer's byte buffer so the native buffer factory can make a copy of the byte array contents + //and package it as a native buffer. + INativeBuffer nativeBuffer = getNativeBufferFactory().getBuffer(transfer.buffer(), System.currentTimeMillis()); + mNativeBufferBroadcaster.broadcast(nativeBuffer); + } + } + + /** + * Threaded LibUsb event processor - continuously polls LibUsb to process events exclusively for this USB tuner + * device using the device context. + */ + class UsbEventProcessor implements Runnable + { + private Thread mThread; + private boolean mProcessing = false; + + /** + * Start the event processing thread + */ + public void start() + { + if(mThread == null) + { + mProcessing = true; + mThread = new Thread(this); + mThread.setName("sdrtrunk USB tuner - bus [" + mBus + "] port [" + mPort + "]"); + mThread.setPriority(Thread.MAX_PRIORITY); + mThread.start(); + } + } + + /** + * Set the stop processing flag and block until the thread stops, blocking up to 1000 ms. + */ + public void stop() + { + mProcessing = false; + + try + { + //Give the thread a second to stop - it should happen quickly because it's only checking transfers + //for completed status and returning them to us to dispatch. + mThread.join(1000); + } + catch(Exception e) + { + mLog.error("Error stopping LibUsb event processing thread - " + e.getLocalizedMessage()); + } + + mThread = null; + } + + /** + * This performs a final handle-events invocation after the event processing thread has been shutdown and + * transfers have been flagged as cancelled. This should cause LibUsb to return all in-progress and now + * canceled transfers back to us via the TransferManager.processTransfer() method. + */ + public void handleFinalEvents() + { + try + { + //Use a short timeout since this is a shutdown operation + LibUsb.handleEventsTimeout(mDeviceContext, 50); + } + catch(Throwable throwable) + { + mLog.error("Error while processing stop-streaming LibUsb timeout events", throwable); + } + } + + /** + * LibUsb event/timeout processing loop + */ + @Override + public void run() + { + mProcessing = true; + + while(mProcessing) + { + try + { + LibUsb.handleEventsTimeout(mDeviceContext, 250); + } + catch(Throwable throwable) + { + mLog.error("Error while processing LibUsb timeout events", throwable); + } + } } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerDevice.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerDevice.java deleted file mode 100644 index 6ae068ac..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerDevice.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package io.github.dsheirer.source.tuner.usb; - -import io.github.dsheirer.source.tuner.TunerClass; - -import javax.usb.UsbDevice; - -public class USBTunerDevice -{ - private UsbDevice mDevice; - private TunerClass mTunerClass; - - public USBTunerDevice( UsbDevice device, TunerClass tunerClass ) - { - mDevice = device; - mTunerClass = tunerClass; - } - - public UsbDevice getDevice() - { - return mDevice; - } - - public TunerClass getTunerClass() - { - return mTunerClass; - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBUtils.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBUtils.java deleted file mode 100644 index 89a982aa..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBUtils.java +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package io.github.dsheirer.source.tuner.usb; - -import javax.usb.UsbConfiguration; -import javax.usb.UsbDevice; -import javax.usb.UsbDisconnectedException; -import javax.usb.UsbEndpoint; -import javax.usb.UsbException; -import javax.usb.UsbHostManager; -import javax.usb.UsbHub; -import javax.usb.UsbInterface; -import javax.usb.UsbServices; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class USBUtils -{ - /** - * Returns a List of currently connected usb devices - */ - public static List getDevices() - throws SecurityException, UsbException, UnsupportedEncodingException - { - UsbServices services = UsbHostManager.getUsbServices(); - - UsbHub root = services.getRootUsbHub(); - - ArrayList devices = new ArrayList(getHubDevices(root)); - - return devices; - } - - /** - * Returns a list of devices attached to the hub - */ - public static List getHubDevices( UsbHub hub ) - throws UnsupportedEncodingException, UsbException - { - ArrayList devices = new ArrayList(); - - @SuppressWarnings( "unchecked" ) - List children = hub.getAttachedUsbDevices(); - - for (UsbDevice child : children) { - if (child.isUsbHub()) { - devices.addAll(getHubDevices((UsbHub) child)); - } else { - devices.add(child); - } - } - - return devices; - } - - public static String getDeviceDetails( UsbDevice device ) - throws UsbException, UnsupportedEncodingException, UsbDisconnectedException - { - StringBuilder sb = new StringBuilder(); - - sb.append(device.getUsbDeviceDescriptor().toString()).append("\n\n"); - - for( Object configObject: device.getUsbConfigurations() ) - { - UsbConfiguration config = (UsbConfiguration)configObject; - - sb.append(config.getUsbConfigurationDescriptor().toString()).append("\n\n"); - - for( Object interfaceObject: config.getUsbInterfaces() ) - { - UsbInterface iface = (UsbInterface)interfaceObject; - - sb.append(iface.getUsbInterfaceDescriptor().toString()).append("\n\n"); - - for( Object endpointObject: iface.getUsbEndpoints() ) - { - UsbEndpoint endpoint = (UsbEndpoint)endpointObject; - - sb.append(endpoint.getUsbEndpointDescriptor().toString()).append("\n\n"); - } - } - } - - return sb.toString(); - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/UsbUtils.java b/src/main/java/io/github/dsheirer/source/tuner/usb/UsbUtils.java new file mode 100644 index 00000000..cd7322b4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/UsbUtils.java @@ -0,0 +1,141 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ +package io.github.dsheirer.source.tuner.usb; + +import javax.usb.UsbConfiguration; +import javax.usb.UsbDevice; +import javax.usb.UsbDisconnectedException; +import javax.usb.UsbEndpoint; +import javax.usb.UsbException; +import javax.usb.UsbHostManager; +import javax.usb.UsbHub; +import javax.usb.UsbInterface; +import javax.usb.UsbServices; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +public class UsbUtils +{ + /** + * Returns a List of currently connected usb devices + */ + public static List getDevices() throws SecurityException, UsbException, UnsupportedEncodingException + { + UsbServices services = UsbHostManager.getUsbServices(); + UsbHub root = services.getRootUsbHub(); + List devices = new ArrayList(getHubDevices(root)); + return devices; + } + + /** + * Returns a list of devices attached to the hub + */ + public static List getHubDevices(UsbHub hub) throws UnsupportedEncodingException, UsbException + { + List devices = new ArrayList(); + List children = hub.getAttachedUsbDevices(); + + for(UsbDevice child : children) + { + if(child.isUsbHub()) + { + devices.addAll(getHubDevices((UsbHub) child)); + } + else + { + devices.add(child); + } + } + + return devices; + } + + public static String getDeviceDetails(UsbDevice device) throws UsbException, UnsupportedEncodingException, UsbDisconnectedException + { + StringBuilder sb = new StringBuilder(); + sb.append(device.getUsbDeviceDescriptor().toString()).append("\n\n"); + + for(Object configObject : device.getUsbConfigurations()) + { + UsbConfiguration config = (UsbConfiguration) configObject; + + sb.append(config.getUsbConfigurationDescriptor().toString()).append("\n\n"); + + for(Object interfaceObject : config.getUsbInterfaces()) + { + UsbInterface iface = (UsbInterface) interfaceObject; + + sb.append(iface.getUsbInterfaceDescriptor().toString()).append("\n\n"); + + for(Object endpointObject : iface.getUsbEndpoints()) + { + UsbEndpoint endpoint = (UsbEndpoint) endpointObject; + + sb.append(endpoint.getUsbEndpointDescriptor().toString()).append("\n\n"); + } + } + } + + return sb.toString(); + } + + public static String getDeviceClass(byte deviceClass) + { + switch(deviceClass) + { + case 0: + return "Unknown Device - Class 0"; + case 2: + return "Communications Device"; + case 3: + return "HID Device"; + case 5: + return "Physical Device"; + case 6: + return "Still Imaging Device"; + case 7: + return "Printer Device"; + case 8: + return "Mass Storage Device"; + case 9: + return "Hub Device"; + case 0xA: + return "Communications Device"; + case 0xB: + return "Smart Card Device"; + case 0xD: + return "Content Security Device"; + case 0xE: + return "Video Device"; + case 0xF: + return "Personal Healthcare Device"; + case 0x10: + return "Audio/Video Device"; + case (byte)0xDC: + return "Diagnostic Device"; + case (byte)0xE0: + return "Wireless Controller Device"; + case (byte)0xEF: + return "Miscellaneous Device"; + default: + return "Unknown Device - Class " + deviceClass; + } + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/ByteSampleConverter.java b/src/main/java/io/github/dsheirer/source/tuner/usb/converter/ByteSampleConverter.java deleted file mode 100644 index 8a20164f..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/ByteSampleConverter.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.source.tuner.usb.converter; - -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; - -public class ByteSampleConverter extends NativeBufferConverter -{ - private final static float[] LOOKUP_VALUES; - - //Creates a static lookup table that converts the 8-bit valued range from 0 - 255 into scaled float values - //of -1.0 to 0 to 1.0 - static - { - LOOKUP_VALUES = new float[256]; - - for(int x = 0; x < 256; x++) - { - LOOKUP_VALUES[x] = (float)(x - 127) / 128.0f; - } - } - - private FloatBuffer mFloatBuffer; - - /** - * Converts native byte buffers containing 8-bit complex samples into complex float samples loaded into a tracked, - * reusable complex sample buffer. Internally tracks the reusable buffer until all downstream consumers have finished - * processing the buffer contents and then reuses the buffer (and memory) for subsequent samples. - */ - public ByteSampleConverter() - { - } - - /** - * Converts the 8-bit complex samples contained in the native buffer into floats that are loaded into a float - * buffer and subsequently transferred to a reusable complex buffer by the parent class. - * - * @param nativeBuffer containing 8-bit complex samples - * @param length of bytes to read from the native buffer - * @return float buffer loaded with converted samples - */ - @Override - protected FloatBuffer convertSamples(ByteBuffer nativeBuffer, int length) - { - nativeBuffer.rewind(); - - if(mFloatBuffer == null || mFloatBuffer.capacity() != nativeBuffer.capacity()) - { - mFloatBuffer = FloatBuffer.allocate(nativeBuffer.capacity()); - } - - mFloatBuffer.rewind(); - - int count = 0; - - while(nativeBuffer.hasRemaining() && count < length) - { - byte sample = nativeBuffer.get(); - count++; - - mFloatBuffer.put(LOOKUP_VALUES[(sample & 0xFF)]); - } - - return mFloatBuffer; - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeBufferConverter.java b/src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeBufferConverter.java deleted file mode 100644 index 61c8b565..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/NativeBufferConverter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ -package io.github.dsheirer.source.tuner.usb.converter; - -import io.github.dsheirer.sample.complex.InterleavedComplexSamples; - -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; - -public abstract class NativeBufferConverter -{ - /** - * Converts native byte buffers into complex float samples and produces reusable complex sample buffers. Tracks - * each reusable buffer until the downstream consumer(s) are finished with the buffer and then reuses the buffer. - */ - public NativeBufferConverter() - { - } - - /** - * Converts the native byte buffer to complex float samples. - * - * @param byteBuffer containing native memory byte samples - * @return native buffer samples converted to interleaved complex floats - */ - public InterleavedComplexSamples convert(ByteBuffer byteBuffer, int length) - { - FloatBuffer floatBuffer = convertSamples(byteBuffer, length); - - float[] samples = new float[floatBuffer.capacity()]; - floatBuffer.rewind(); - floatBuffer.get(samples); - - return new InterleavedComplexSamples(samples, System.currentTimeMillis()); - } - - /** - * Converts the native byte buffer bytes into complex float samples. - * - * @param buffer containing native byte buffer samples - * @return a buffer with complex float samples. This float buffer will not be modified, therefore sub-class - * implementations can reuse the float buffer. - */ - protected abstract FloatBuffer convertSamples(ByteBuffer buffer, int length); -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/SignedByteSampleConverter.java b/src/main/java/io/github/dsheirer/source/tuner/usb/converter/SignedByteSampleConverter.java deleted file mode 100644 index 5096aa46..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/converter/SignedByteSampleConverter.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ -package io.github.dsheirer.source.tuner.usb.converter; - -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; - -public class SignedByteSampleConverter extends NativeBufferConverter -{ - private final static float[] LOOKUP_VALUES; - - //Creates a static lookup table that converts the signed 8-bit valued range from -128 to 127 into scaled float values - //of -1.0 to 0 to 1.0 - static - { - LOOKUP_VALUES = new float[256]; - - for(int x = 0; x < 256; x++) - { - LOOKUP_VALUES[x] = (float)((byte)x) / 128.0f; - } - } - - private FloatBuffer mFloatBuffer; - - /** - * Converts native byte buffers containing signed 8-bit complex samples into complex float samples loaded into a tracked, - * reusable complex sample buffer. Internally tracks the reusable buffer until all downstream consumers have finished - * processing the buffer contents and then reuses the buffer (and memory) for subsequent samples. - */ - public SignedByteSampleConverter() - { - } - - /** - * Converts the signed 8-bit complex samples contained in the native buffer into floats that are loaded into a float - * buffer and subsequently transferred to a reusable complex buffer by the parent class. - * - * @param nativeBuffer containing signed 8-bit complex samples - * @param length of bytes to read from the native buffer - * @return float buffer loaded with converted samples - */ - @Override - protected FloatBuffer convertSamples(ByteBuffer nativeBuffer, int length) - { - nativeBuffer.rewind(); - - if(mFloatBuffer == null || mFloatBuffer.capacity() != nativeBuffer.capacity()) - { - mFloatBuffer = FloatBuffer.allocate(nativeBuffer.capacity()); - } - - mFloatBuffer.rewind(); - - int count = 0; - - while(nativeBuffer.hasRemaining() && count < length) - { - byte sample = nativeBuffer.get(); - count++; - - mFloatBuffer.put(LOOKUP_VALUES[(sample & 0xFF)]); - } - - return mFloatBuffer; - } - - public static void main(String[] args) - { - SignedByteSampleConverter converter = new SignedByteSampleConverter(); - int a = 0; - } -} diff --git a/src/main/java/io/github/dsheirer/spectrum/ShowTunerMenuItem.java b/src/main/java/io/github/dsheirer/spectrum/ShowTunerMenuItem.java index 4e23e4b7..bfd510ca 100644 --- a/src/main/java/io/github/dsheirer/spectrum/ShowTunerMenuItem.java +++ b/src/main/java/io/github/dsheirer/spectrum/ShowTunerMenuItem.java @@ -1,30 +1,27 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.spectrum; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerEvent; -import io.github.dsheirer.source.tuner.TunerModel; +import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; import javax.swing.JMenuItem; import java.awt.EventQueue; @@ -33,13 +30,13 @@ import java.awt.event.ActionListener; public class ShowTunerMenuItem extends JMenuItem { - private TunerModel mTunerModel; + private DiscoveredTunerModel mDiscoveredTunerModel; private Tuner mTuner; - public ShowTunerMenuItem(TunerModel tunerModel, Tuner tuner) + public ShowTunerMenuItem(DiscoveredTunerModel discoveredTunerModel, Tuner tuner) { - super(tuner != null ? "Show: " + tuner.getName() : "(empty)"); - mTunerModel = tunerModel; + super(tuner != null ? "Show: " + tuner.getPreferredName() : "(empty)"); + mDiscoveredTunerModel = discoveredTunerModel; mTuner = tuner; addActionListener(new ActionListener() @@ -52,7 +49,7 @@ public class ShowTunerMenuItem extends JMenuItem @Override public void run() { - mTunerModel.broadcast(new TunerEvent(mTuner, TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); + mDiscoveredTunerModel.broadcast(new TunerEvent(mTuner, TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); SystemProperties properties = SystemProperties.getInstance(); properties.set(SpectralDisplayPanel.SPECTRAL_DISPLAY_ENABLED, true); } diff --git a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayAdjuster.java b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayAdjuster.java index b062ded5..fdc7f727 100644 --- a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayAdjuster.java +++ b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayAdjuster.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.spectrum; import io.github.dsheirer.dsp.filter.smoothing.SmoothingFilter.SmoothingType; diff --git a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java index b992b108..7cca7817 100644 --- a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java @@ -36,7 +36,8 @@ import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.Tuner; -import io.github.dsheirer.source.tuner.TunerModel; +import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; +import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; import io.github.dsheirer.spectrum.OverlayPanel.ChannelDisplay; import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter; import io.github.dsheirer.spectrum.converter.DFTResultsConverter; @@ -105,7 +106,7 @@ public class SpectralDisplayPanel extends JPanel private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; private SettingsManager mSettingsManager; - private TunerModel mTunerModel; + private DiscoveredTunerModel mDiscoveredTunerModel; private Tuner mTuner; /** @@ -120,12 +121,12 @@ public class SpectralDisplayPanel extends JPanel * the DFT is translated to decibels for display in the spectrum and * waterfall components. */ - public SpectralDisplayPanel(PlaylistManager playlistManager, SettingsManager settingsManager, TunerModel tunerModel) + public SpectralDisplayPanel(PlaylistManager playlistManager, SettingsManager settingsManager, DiscoveredTunerModel discoveredTunerModel) { mChannelModel = playlistManager.getChannelModel(); mChannelProcessingManager = playlistManager.getChannelProcessingManager(); mSettingsManager = settingsManager; - mTunerModel = tunerModel; + mDiscoveredTunerModel = discoveredTunerModel; mSpectrumPanel = new SpectrumPanel(mSettingsManager); mOverlayPanel = new OverlayPanel(mSettingsManager, mChannelModel, mChannelProcessingManager); @@ -442,7 +443,6 @@ public class SpectralDisplayPanel extends JPanel //Fire frequency and sample rate change events so that the spectrum //and overlay panels can synchronize process(SourceEvent.frequencyChange(null, mTuner.getTunerController().getFrequency())); - process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate())); } } @@ -455,7 +455,7 @@ public class SpectralDisplayPanel extends JPanel if(mTuner != null) { //Deregister for frequency change events from the tuner - mTuner.getTunerController().removeListener(this); + mTuner.getTunerController().removeListener(SpectralDisplayPanel.this); //Deregister the dft processor from receiving samples mTuner.getTunerController().removeBufferListener(mComplexDftProcessor); @@ -468,6 +468,14 @@ public class SpectralDisplayPanel extends JPanel mWaterfallPanel.clearWaterfall(); } + /** + * Currently displayed tuner + */ + public Tuner getTuner() + { + return mTuner; + } + /** * Monitors the sizing of the layered pane and resizes the spectrum and * channel panels whenever the layered pane is resized @@ -756,9 +764,9 @@ public class SpectralDisplayPanel extends JPanel boolean separatorAdded = false; - for(Tuner tuner : mTunerModel.getTuners()) + for(DiscoveredTuner discoveredTuner : mDiscoveredTunerModel.getAvailableTuners()) { - if(mTuner == null || mTuner != tuner) + if(mTuner == null || mTuner != discoveredTuner.getTuner()) { if(!separatorAdded) { @@ -766,7 +774,7 @@ public class SpectralDisplayPanel extends JPanel separatorAdded = true; } - contextMenu.add(new ShowTunerMenuItem(mTunerModel, tuner)); + contextMenu.add(new ShowTunerMenuItem(mDiscoveredTunerModel, discoveredTuner.getTuner())); } } diff --git a/src/main/java/io/github/dsheirer/spectrum/SpectrumFrame.java b/src/main/java/io/github/dsheirer/spectrum/SpectrumFrame.java index 54eeea40..7a257a79 100644 --- a/src/main/java/io/github/dsheirer/spectrum/SpectrumFrame.java +++ b/src/main/java/io/github/dsheirer/spectrum/SpectrumFrame.java @@ -1,24 +1,27 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ package io.github.dsheirer.spectrum; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.tuner.Tuner; -import io.github.dsheirer.source.tuner.TunerModel; +import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; import net.miginfocom.swing.MigLayout; import javax.swing.JFrame; @@ -32,15 +35,16 @@ public class SpectrumFrame extends JFrame implements WindowListener private SpectralDisplayPanel mSpectralDisplayPanel; - public SpectrumFrame(PlaylistManager playlistManager, SettingsManager settingsManager, TunerModel tunerModel, Tuner tuner) + public SpectrumFrame(PlaylistManager playlistManager, SettingsManager settingsManager, + DiscoveredTunerModel discoveredTunerModel, Tuner tuner) { - setTitle("SDRTRunk [" + tuner.getName() + "]"); + setTitle("SDRTRunk [" + tuner.getPreferredName() + "]"); setBounds(100, 100, 1280, 600); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setLayout(new MigLayout("insets 0 0 0 0", "[grow]", "[grow]")); - mSpectralDisplayPanel = new SpectralDisplayPanel(playlistManager, settingsManager, tunerModel); + mSpectralDisplayPanel = new SpectralDisplayPanel(playlistManager, settingsManager, discoveredTunerModel); mSpectralDisplayPanel.showTuner(tuner); add(mSpectralDisplayPanel, "grow"); diff --git a/src/main/java/io/github/dsheirer/spectrum/SpectrumPanel.java b/src/main/java/io/github/dsheirer/spectrum/SpectrumPanel.java index 32ae17a0..c4262a0b 100644 --- a/src/main/java/io/github/dsheirer/spectrum/SpectrumPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/SpectrumPanel.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.spectrum; import io.github.dsheirer.dsp.filter.smoothing.GaussianSmoothingFilter; diff --git a/src/main/java/io/github/dsheirer/util/SwingUtils.java b/src/main/java/io/github/dsheirer/util/SwingUtils.java new file mode 100644 index 00000000..daf6037a --- /dev/null +++ b/src/main/java/io/github/dsheirer/util/SwingUtils.java @@ -0,0 +1,43 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.util; + +import java.awt.EventQueue; + +/** + * Swing utilities + */ +public class SwingUtils +{ + /** + * Runs the runnable on the swing dispatch thread + */ + public static void run(Runnable r) + { + if(EventQueue.isDispatchThread()) + { + r.run(); + } + else + { + EventQueue.invokeLater(r); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/mapviewer/WaypointPainter.java b/src/main/java/org/jdesktop/swingx/mapviewer/WaypointPainter.java index f5f0319b..17d31214 100644 --- a/src/main/java/org/jdesktop/swingx/mapviewer/WaypointPainter.java +++ b/src/main/java/org/jdesktop/swingx/mapviewer/WaypointPainter.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + /* * WaypointMapOverlay.java * @@ -12,7 +31,8 @@ package org.jdesktop.swingx.mapviewer; import org.jdesktop.swingx.JXMapViewer; import org.jdesktop.swingx.painter.AbstractPainter; -import java.awt.*; +import java.awt.Graphics2D; +import java.awt.Rectangle; import java.util.Collections; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/org/jdesktop/swingx/mapviewer/WaypointRenderer.java b/src/main/java/org/jdesktop/swingx/mapviewer/WaypointRenderer.java index 4b72cdfd..d7161de5 100644 --- a/src/main/java/org/jdesktop/swingx/mapviewer/WaypointRenderer.java +++ b/src/main/java/org/jdesktop/swingx/mapviewer/WaypointRenderer.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + /* * WaypointRenderer.java * @@ -11,7 +30,7 @@ package org.jdesktop.swingx.mapviewer; import org.jdesktop.swingx.JXMapViewer; -import java.awt.*; +import java.awt.Graphics2D; /** * A interface that draws waypoints. Implementations of WaypointRenderer can diff --git a/src/main/java/org/jdesktop/swingx/painter/Painter.java b/src/main/java/org/jdesktop/swingx/painter/Painter.java index 67ddb7f5..ac51207a 100644 --- a/src/main/java/org/jdesktop/swingx/painter/Painter.java +++ b/src/main/java/org/jdesktop/swingx/painter/Painter.java @@ -1,27 +1,25 @@ /* - * $Id: Painter.java 3860 2010-10-26 01:14:53Z kschaefe $ + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package org.jdesktop.swingx.painter; -import java.awt.*; +import java.awt.Graphics2D; /** *

A painting delegate. The Painter interface defines exactly one method, diff --git a/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java b/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java index 11de119d..f5f50289 100644 --- a/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java +++ b/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java @@ -1,42 +1,37 @@ /* - * $Id: GraphicsUtilities.java 4082 2011-11-15 18:39:43Z kschaefe $ + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package org.jdesktop.swingx.util; import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; +import javax.swing.JComponent; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Insets; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Transparency; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster;