Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
64da45fdde |
40
README.md
40
README.md
|
|
@ -23,6 +23,7 @@ A license hasn't been selected yet, though it's likely to be GPL - have to make
|
|||
- AM demodulation
|
||||
- Run-time manipulation of scan database and config
|
||||
- Temporary channel lockout
|
||||
- SDR configuration
|
||||
#### Long term:
|
||||
- P25 trunking
|
||||
- DMR trunking
|
||||
|
|
@ -74,21 +75,21 @@ Depending on your preferred build system
|
|||
|
||||
There are three options available for audio output: PulseAudio, ALSA, JACK, and OSS. By default ALSA is the selected library. Your preference can be set by running one of the following commands from the `build` directory:
|
||||
|
||||
cmake .. -DUSE_AUDIO_PULSE=ON
|
||||
cmake .. -DUSE_AUDIO_ALSA=ON
|
||||
cmake .. -DUSE_AUDIO_JACK=ON
|
||||
cmake ../src -DUSE_AUDIO_PULSE=ON
|
||||
cmake ../src -DUSE_AUDIO_ALSA=ON
|
||||
cmake ../src -DUSE_AUDIO_JACK=ON
|
||||
Do note that PulseAudio uses significantly more CPU than ALSA when streaming PiScan audio.
|
||||
|
||||
Additionally, some versions of liquid, particularly `libliquid2d` and `libliquid-dev` versions `1.3.2` and greater, have a different API, resulting in build errors in the "Modem" files of the `cubic` module. If that happens, try running this from `build`:
|
||||
Additionally, some versions of liquid, particularly `libliquid-dev` versions `1.3.2` and greater, have a different API, resulting in build errors in the "Modem" files of the `cubic` module. If that happens, try running this from `build`:
|
||||
|
||||
cmake .. -DLIQUID_API_OLD=OFF
|
||||
cmake ../src -DLIQUID_API_OLD=OFF
|
||||
### Building
|
||||
Once the environment is properly configured, `cd` into `build` and build the LiveMedia library:
|
||||
|
||||
make live555
|
||||
If the build fails: If you are building in a shared folder in a VM on a Windows host, you must download and extract the Live sourcefiles manually into `src/external/live` and run the command below before trying again. This is due to Windows not playing nicely with permissions and symbolic links
|
||||
|
||||
cmake .. -DHGFS=ON
|
||||
cmake ../src -DHGFS=ON
|
||||
Now you can build PiScan:
|
||||
|
||||
make all
|
||||
|
|
@ -97,20 +98,13 @@ Alternatively, you can build the binaries individually if you don't need all of
|
|||
make piscan_server # main program
|
||||
|
||||
make piscan_hpdconv # tool to convert Uniden Sentinel files to PiScan files
|
||||
Once the build is complete, you can run the install script:
|
||||
|
||||
sudo make install
|
||||
Note: install must be re-run if the project is rebuilt
|
||||
|
||||
### Running
|
||||
PiScan can now be run with the command:
|
||||
For now, you have to be in the `build` directory to run the program. In the future there will be an install command to eliminate the need for this.
|
||||
You can now run PiScan from the command line:
|
||||
|
||||
piscan_server [args]
|
||||
./src/piscan_server
|
||||
See **Usage** for more information on command arguments and setting up the data files
|
||||
|
||||
Alternatively, if you prefer not to use `make install` you can run it locally from the `build` directory with the command:
|
||||
|
||||
./src/piscan_server [args]
|
||||
|
||||
|
||||
## Usage
|
||||
### Command Arguments
|
||||
|
|
@ -125,18 +119,6 @@ All data used by PiScan is stored in its working directory (this is the `data` d
|
|||
- `systems.json` contains the scan database - more on what that looks like below
|
||||
On the first run of PiScan, these files likely won't exist. It will continue running with default parameters, and a config and state file will be generated with these defaults when the program ends.
|
||||
If there is no scan file, PiScan cannot scan so it will instead hold at 100MHz. It will not allow the user to scan, but will allow manual frequency tuning.
|
||||
|
||||
#### SDR configuration
|
||||
PiScan allows for configuring specific RTL dongles to run with it. By default, it doesn't have any pre-configured SDR's, so it will select the first available device and save it to the config file. One it's in the config file it will be given a rank (for specifying the order PiScan chooses from configured devies) and a descriptor, which contains the device name and serial number. There, the dongle's PPM correction and preferred sample rate can be set. The format of each device config is as such:
|
||||
|
||||
{
|
||||
"rank": "0",
|
||||
"descriptor": "Generic RTL2832U OEM :: 00000001",
|
||||
"driver": "rtlsdr",
|
||||
"ppm_correction": "0",
|
||||
"sample_rate": "2048000"
|
||||
}
|
||||
|
||||
#### Scan Database File
|
||||
`systems.json` MUST adhere to this format (minus the comments) for PiScan to read it correctly. Use the sample file in `data/defaults` as a starting point for your database if writing it manually.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from threading import Thread
|
|||
|
||||
from time import sleep
|
||||
|
||||
import ui_scan_client
|
||||
import audio_manager
|
||||
import common
|
||||
import constants
|
||||
|
|
@ -34,13 +35,9 @@ class PiScanClient(QWidget, common.AppInterface):
|
|||
def __init__(self, parent=None, address=None, port=None, use_audio=False, rtsp_port=None):
|
||||
super(PiScanClient, self).__init__(parent)
|
||||
common.setInstance(self)
|
||||
ui_file = 'scan_client.ui'
|
||||
ui_file = QFile(ui_file)
|
||||
ui_file.open(QFile.ReadOnly)
|
||||
|
||||
loader = QUiLoader()
|
||||
self.window = loader.load(ui_file)
|
||||
ui_file.close()
|
||||
self.window = ui_scan_client.Ui_Form()
|
||||
self.window.setupUi(self)
|
||||
|
||||
self.parentWindow = parent
|
||||
#layout = QGridLayout(self)
|
||||
|
|
@ -53,7 +50,7 @@ class PiScanClient(QWidget, common.AppInterface):
|
|||
self.scanner = scanner.Scanner(self.window)
|
||||
self.dialogs = dialogs.Dialogs(self.window)
|
||||
|
||||
self.contextStack = self.window.findChild(QStackedWidget, 'contextStack')
|
||||
self.contextStack = self.window.contextStack
|
||||
#self.setWindowMode(common.WindowMode.CONNECT)
|
||||
self.showConnectDialog()
|
||||
#self.setWindowMode(common.WindowMode.SCANNER)
|
||||
|
|
@ -280,12 +277,11 @@ class HostWindow(QtWidgets.QMainWindow):
|
|||
|
||||
form = PiScanClient(self, address, port, use_audio, rtsp_port)
|
||||
|
||||
mainWidget = form.mainWidget()
|
||||
self.setPalette(mainWidget.palette())
|
||||
self.setGeometry(mainWidget.geometry())
|
||||
self.setWindowTitle(mainWidget.windowTitle())
|
||||
self.setPalette(form.palette())
|
||||
self.setGeometry(form.geometry())
|
||||
self.setWindowTitle(form.windowTitle())
|
||||
|
||||
self.setCentralWidget(mainWidget)
|
||||
self.setCentralWidget(form)
|
||||
#self.actionQuit.triggered.connect(self.closeEvent)
|
||||
|
||||
#self.show()
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ import constants
|
|||
|
||||
class ConnectDialog:
|
||||
def __init__(self, parentWindow, address=None, port=None, use_audio=False, rtsp_port=None):
|
||||
self.widget = parentWindow.findChild(QWidget, 'connectPage')
|
||||
self.errorLabel = parentWindow.findChild(QLabel, 'connect_errorLabel')
|
||||
self.confirmButton = parentWindow.findChild(QPushButton, 'connect_confirmButton')
|
||||
self.connectIndicator = parentWindow.findChild(QLabel, 'connect_indicator')
|
||||
self.hostLineEdit = parentWindow.findChild(QLineEdit, 'connect_hostnameLineEdit')
|
||||
self.portLineEdit = parentWindow.findChild(QLineEdit, 'connect_portLineEdit')
|
||||
self.logo = parentWindow.findChild(QLabel, 'connect_logoImage')
|
||||
self.hostLabel = parentWindow.findChild(QLabel, 'hostLabel')
|
||||
self.portLabel = parentWindow.findChild(QLabel, 'hostPortLabel')
|
||||
self.audioCheckBox = parentWindow.findChild(QCheckBox, 'connect_audioCheckBox')
|
||||
self.rtspPortPanel = parentWindow.findChild(QWidget, 'connect_rtspPortPanel')
|
||||
self.rtspPortLineEdit = parentWindow.findChild(QLineEdit, 'connect_rtspPortLineEdit')
|
||||
self.widget = parentWindow.connectPage
|
||||
self.errorLabel = parentWindow.connect_errorLabel
|
||||
self.confirmButton = parentWindow.connect_confirmButton
|
||||
self.connectIndicator = parentWindow.connect_indicator
|
||||
self.hostLineEdit = parentWindow.connect_hostnameLineEdit
|
||||
self.portLineEdit = parentWindow.connect_portLineEdit
|
||||
self.logo = parentWindow.connect_logoImage
|
||||
self.hostLabel = parentWindow.hostLabel
|
||||
self.portLabel = parentWindow.hostPortLabel
|
||||
self.audioCheckBox = parentWindow.connect_audioCheckBox
|
||||
self.rtspPortPanel = parentWindow.connect_rtspPortPanel
|
||||
self.rtspPortLineEdit = parentWindow.connect_rtspPortLineEdit
|
||||
|
||||
self.logo.setPixmap(QPixmap("resources/icon-256.png"))
|
||||
self.logo.setVisible(False)
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ class Dialogs:
|
|||
dialogStack = deque()
|
||||
|
||||
def __init__(self, parent):
|
||||
self.widget = parent.findChild(QWidget, 'dialogsPage')
|
||||
self.header = parent.findChild(QWidget, 'dialogHeader')
|
||||
self.backButton = parent.findChild(QPushButton, 'dialog_backButton')
|
||||
self.titleLabel = parent.findChild(QLabel, 'dialog_titleLabel')
|
||||
self.dialogStackWidget = parent.findChild(QStackedWidget, 'dialogsStack')
|
||||
self.widget = parent.dialogsPage
|
||||
self.header = parent.dialogHeader
|
||||
self.backButton = parent.dialog_backButton
|
||||
self.titleLabel = parent.dialog_titleLabel
|
||||
self.dialogStackWidget = parent.dialogsStack
|
||||
|
||||
self.backButton.clicked.connect(self.dialogReturn)
|
||||
self.dialogStackWidget.currentChanged.connect(self.updateTitle)
|
||||
|
|
@ -47,10 +47,10 @@ class Dialogs:
|
|||
|
||||
class ManualEntry:
|
||||
def __init__(self, parentWindow, dialogHost):
|
||||
self.widget = parentWindow.findChild(QWidget, 'manualEntryDialog')
|
||||
self.confirmButton = parentWindow.findChild(QPushButton, 'manual_confirmButton')
|
||||
self.freqLineEdit = parentWindow.findChild(QLineEdit, 'manual_freqLineEdit')
|
||||
self.modulationCombo = parentWindow.findChild(QComboBox, 'manual_modulationCombo')
|
||||
self.widget = parentWindow.manualEntryDialog
|
||||
self.confirmButton = parentWindow.manual_confirmButton
|
||||
self.freqLineEdit = parentWindow.manual_freqLineEdit
|
||||
self.modulationCombo = parentWindow.manual_modulationCombo
|
||||
self.host = dialogHost
|
||||
|
||||
self.confirmButton.clicked.connect(self.onConfirm)
|
||||
|
|
@ -68,12 +68,12 @@ class Dialogs:
|
|||
|
||||
class EditEntry:
|
||||
def __init__(self, parent):
|
||||
self.widget = parent.findChild(QWidget, 'editEntryDialog')
|
||||
self.codeLineEdit = parent.findChild(QLineEdit, 'entry_codeLineEdit')
|
||||
self.delayLineEdit = parent.findChild(QLineEdit, 'entry_delayLineEdit')
|
||||
self.freqLineEdit = parent.findChild(QLineEdit, 'entry_freqLineEdit')
|
||||
self.lockoutCheckbox = parent.findChild(QCheckBox, 'entry_lockoutCheckbox')
|
||||
self.modulationCombo = parent.findChild(QComboBox, 'entry_modulationCombo')
|
||||
self.systemCombo = parent.findChild(QComboBox, 'entry_systemCombo')
|
||||
self.tagLineEdit = parent.findChild(QLineEdit, 'entry_tagLineEdit')
|
||||
self.widget = parent.editEntryDialog
|
||||
self.codeLineEdit = parent.entry_codeLineEdit
|
||||
self.delayLineEdit = parent.entry_delayLineEdit
|
||||
self.freqLineEdit = parent.entry_freqLineEdit
|
||||
self.lockoutCheckbox = parent.entry_lockoutCheckbox
|
||||
self.modulationCombo = parent.entry_modulationCombo
|
||||
self.systemCombo = parent.entry_systemCombo
|
||||
self.tagLineEdit = parent.entry_tagLineEdit
|
||||
|
||||
|
|
|
|||
|
|
@ -4917,7 +4917,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>179</width>
|
||||
<width>443</width>
|
||||
<height>223</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -17,43 +17,41 @@ class Scanner:
|
|||
squelchFromContext = False
|
||||
|
||||
def __init__(self, parentWindow):
|
||||
self.widget = parentWindow.findChild(QWidget, 'scannerPage')
|
||||
self.contextStack = parentWindow.findChild(QStackedWidget, 'scanContextStack')
|
||||
self.holdPage = parentWindow.findChild(QWidget, 'scanner_holdPage')
|
||||
self.scanPage = parentWindow.findChild(QWidget, 'scanner_scanPage')
|
||||
self.entryTagLabel = parentWindow.findChild(QLabel, 'scanner_entryTagLabel')
|
||||
self.frequencyLabel = parentWindow.findChild(QLabel, 'scanner_frequencyLabel')
|
||||
self.modulationLabel = parentWindow.findChild(QLabel, 'scanner_modulationLabel')
|
||||
self.systemTagLabel = parentWindow.findChild(QLabel, 'scanner_systemTagLabel')
|
||||
self.entryNumLabel = parentWindow.findChild(QLabel, 'scanner_entryNumLabel')
|
||||
self.delayLabel = parentWindow.findChild(QLabel, 'scanner_delayLabel')
|
||||
self.lockoutDurationLabel = parentWindow.findChild(QLabel, 'scanner_lockoutDurationLabel')
|
||||
self.scanModeLabel = parentWindow.findChild(QLabel, 'scanner_scanModeLabel')
|
||||
self.lockoutDurationButton = parentWindow.findChild(QPushButton, 'scanner_lockoutDurationButton')
|
||||
self.scanIndicator = parentWindow.findChild(QLabel, 'scanner_scanIndicator')
|
||||
self.gainSlider = parentWindow.findChild(QSlider, 'scanner_gainSlider')
|
||||
##self.gainLabel = parentWindow.findChild(QLabel, 'scanner_gainLabel')
|
||||
self.sigStrengthBar = parentWindow.findChild(QProgressBar, 'scanner_sigStrengthBar')
|
||||
self.squelchSlider = parentWindow.findChild(QSlider, 'scanner_squelchSlider')
|
||||
##self.squelchLabel = parentWindow.findChild(QLabel, 'scanner_squelchLabel')
|
||||
self.fnButtonsWidget = parentWindow.findChild(QWidget, 'scanner_fnButtonsWidget')
|
||||
self.fnButton1 = parentWindow.findChild(QPushButton, 'scanner_fnButton1')
|
||||
self.fnButton2 = parentWindow.findChild(QPushButton, 'scanner_fnButton2')
|
||||
self.fnButton3 = parentWindow.findChild(QPushButton, 'scanner_fnButton3')
|
||||
self.fnButton4 = parentWindow.findChild(QPushButton, 'scanner_fnButton4')
|
||||
self.widget = parentWindow.scannerPage
|
||||
self.contextStack = parentWindow.scanContextStack
|
||||
self.holdPage = parentWindow.scanner_holdPage
|
||||
self.scanPage = parentWindow.scanner_scanPage
|
||||
self.entryTagLabel = parentWindow.scanner_entryTagLabel
|
||||
self.frequencyLabel = parentWindow.scanner_frequencyLabel
|
||||
self.modulationLabel = parentWindow.scanner_modulationLabel
|
||||
self.systemTagLabel = parentWindow.scanner_systemTagLabel
|
||||
self.entryNumLabel = parentWindow.scanner_entryNumLabel
|
||||
self.delayLabel = parentWindow.scanner_delayLabel
|
||||
self.lockoutDurationLabel = parentWindow.scanner_lockoutDurationLabel
|
||||
self.scanModeLabel = parentWindow.scanner_scanModeLabel
|
||||
self.lockoutDurationButton = parentWindow.scanner_lockoutDurationButton
|
||||
self.scanIndicator = parentWindow.scanner_scanIndicator
|
||||
self.gainSlider = parentWindow.scanner_gainSlider
|
||||
self.sigStrengthBar = parentWindow.scanner_sigStrengthBar
|
||||
self.squelchSlider = parentWindow.scanner_squelchSlider
|
||||
self.fnButtonsWidget = parentWindow.scanner_fnButtonsWidget
|
||||
self.fnButton1 = parentWindow.scanner_fnButton1
|
||||
self.fnButton2 = parentWindow.scanner_fnButton2
|
||||
self.fnButton3 = parentWindow.scanner_fnButton3
|
||||
self.fnButton4 = parentWindow.scanner_fnButton4
|
||||
|
||||
self.sidebarToggleButton = parentWindow.findChild(QToolButton, 'scanner_sidebarToggle')
|
||||
self.sidebarPanel = parentWindow.findChild(QWidget, 'scanner_sidebarPanel')
|
||||
self.sidebarToggleButton = parentWindow.scanner_sidebarToggle
|
||||
self.sidebarPanel = parentWindow.scanner_sidebarPanel
|
||||
|
||||
self.disconnectButton = parentWindow.findChild(QToolButton, 'scanner_disconnectButton')
|
||||
self.settingsButton = parentWindow.findChild(QToolButton, 'scanner_settingsButton')
|
||||
self.connectInfoButton = parentWindow.findChild(QToolButton, 'scanner_connectInfoButton')
|
||||
self.disconnectButton = parentWindow.scanner_disconnectButton
|
||||
self.settingsButton = parentWindow.scanner_settingsButton
|
||||
self.connectInfoButton = parentWindow.scanner_connectInfoButton
|
||||
|
||||
self.volumeControlPanel = parentWindow.findChild(QWidget, 'scanner_volumeControl')
|
||||
self.volumeSlider = parentWindow.findChild(QWidget, 'scanner_volumeSlider')
|
||||
self.muteButton = parentWindow.findChild(QWidget, 'scanner_volumeMute')
|
||||
self.volumeControlPanel = parentWindow.scanner_volumeControl
|
||||
self.volumeSlider = parentWindow.scanner_volumeSlider
|
||||
self.muteButton = parentWindow.scanner_volumeMute
|
||||
|
||||
self.entryEditButton = parentWindow.findChild(QToolButton, 'scanner_entryEditButton')
|
||||
self.entryEditButton = parentWindow.scanner_entryEditButton
|
||||
|
||||
self.fnButton1.clicked.connect(self.onFnButton1)
|
||||
self.fnButton2.clicked.connect(self.onFnButton2)
|
||||
|
|
@ -81,7 +79,7 @@ class Scanner:
|
|||
self.entryEditButton.setVisible(False)
|
||||
self.delayLabel.setVisible(False)
|
||||
self.scanModeLabel.setVisible(False)
|
||||
parentWindow.findChild(QWidget, 'line_4').setVisible(False)
|
||||
parentWindow.line_4.setVisible(False)
|
||||
|
||||
movie = QMovie("resources/bar-scan.gif")
|
||||
movie.start()
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in New Issue