This commit is contained in:
parent
4cc9e8b2d2
commit
10b44cdf72
81
protocol_prototype/DryBox/README.md
Normal file
81
protocol_prototype/DryBox/README.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# DryBox - Secure Voice Communication System
|
||||||
|
|
||||||
|
A PyQt5-based application demonstrating secure voice communication using the Noise XK protocol, Codec2 audio compression, and 4FSK modulation.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Secure Communication**: End-to-end encryption using Noise XK protocol
|
||||||
|
- **Audio Compression**: Codec2 (3200bps) for efficient voice transmission
|
||||||
|
- **Modulation**: 4FSK (4-level Frequency Shift Keying) for robust transmission
|
||||||
|
- **GSM Network Simulation**: Simulates realistic GSM network conditions
|
||||||
|
- **Real-time Audio**: Playback and recording capabilities
|
||||||
|
- **Visual Feedback**: Waveform displays and signal strength indicators
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.7+
|
||||||
|
- PyQt5
|
||||||
|
- NumPy
|
||||||
|
- pycodec2
|
||||||
|
- Additional dependencies in `requirements.txt`
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install system dependencies:
|
||||||
|
```bash
|
||||||
|
./install_audio_deps.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install Python dependencies:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Application
|
||||||
|
|
||||||
|
Simply run:
|
||||||
|
```bash
|
||||||
|
python3 UI/main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The application will automatically:
|
||||||
|
- Start the GSM network simulator
|
||||||
|
- Initialize two phone clients
|
||||||
|
- Display the main UI with GSM status panel
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Phone Controls
|
||||||
|
- **Click "Call" button** or press `1`/`2` to initiate/answer calls
|
||||||
|
- **Ctrl+1/2**: Toggle audio playback for each phone
|
||||||
|
- **Alt+1/2**: Toggle audio recording for each phone
|
||||||
|
|
||||||
|
### GSM Settings
|
||||||
|
- **Click "Settings" button** or press `Ctrl+G` to open GSM settings dialog
|
||||||
|
- Adjust signal strength, quality, noise, and network parameters
|
||||||
|
- Use presets for quick configuration (Excellent/Good/Fair/Poor)
|
||||||
|
|
||||||
|
### Other Controls
|
||||||
|
- **Space**: Run automatic test sequence
|
||||||
|
- **Ctrl+L**: Clear debug console
|
||||||
|
- **Ctrl+A**: Audio processing options menu
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **main.py**: Main UI application
|
||||||
|
- **phone_manager.py**: Manages phone instances and audio
|
||||||
|
- **protocol_phone_client.py**: Implements the secure protocol stack
|
||||||
|
- **noise_wrapper.py**: Noise XK protocol implementation
|
||||||
|
- **gsm_simulator.py**: Network simulation relay
|
||||||
|
- **gsm_status_widget.py**: Real-time GSM status display
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The automatic test feature (`Space` key) runs through a complete call sequence:
|
||||||
|
1. Initial state verification
|
||||||
|
2. Call initiation
|
||||||
|
3. Call answering
|
||||||
|
4. Noise XK handshake
|
||||||
|
5. Voice session establishment
|
||||||
|
6. Audio transmission
|
||||||
|
7. Call termination
|
@ -1,4 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import atexit
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QPushButton, QLabel, QFrame, QSizePolicy, QStyle, QTextEdit, QSplitter,
|
QPushButton, QLabel, QFrame, QSizePolicy, QStyle, QTextEdit, QSplitter,
|
||||||
@ -36,6 +39,11 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
# GSM simulation timer
|
# GSM simulation timer
|
||||||
self.gsm_simulation_timer = None
|
self.gsm_simulation_timer = None
|
||||||
|
|
||||||
|
# GSM simulator process
|
||||||
|
self.gsm_simulator_process = None
|
||||||
|
self.start_gsm_simulator()
|
||||||
|
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QMainWindow {
|
QMainWindow {
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
@ -134,9 +142,8 @@ class PhoneUI(QMainWindow):
|
|||||||
# Setup debug signal early
|
# Setup debug signal early
|
||||||
self.debug_signal.connect(self.append_debug)
|
self.debug_signal.connect(self.append_debug)
|
||||||
|
|
||||||
self.manager = PhoneManager()
|
# Initialize phone manager after simulator starts
|
||||||
self.manager.ui = self # Set UI reference for debug logging
|
QTimer.singleShot(1000, self.initialize_phone_manager)
|
||||||
self.manager.initialize_phones()
|
|
||||||
|
|
||||||
# Main widget with splitter
|
# Main widget with splitter
|
||||||
main_widget = QWidget()
|
main_widget = QWidget()
|
||||||
@ -179,31 +186,10 @@ class PhoneUI(QMainWindow):
|
|||||||
phones_layout.addWidget(protocol_info)
|
phones_layout.addWidget(protocol_info)
|
||||||
|
|
||||||
# Phone displays layout
|
# Phone displays layout
|
||||||
phone_controls_layout = QHBoxLayout()
|
self.phone_controls_layout = QHBoxLayout()
|
||||||
phone_controls_layout.setSpacing(20)
|
self.phone_controls_layout.setSpacing(20)
|
||||||
phone_controls_layout.setContentsMargins(10, 0, 10, 0)
|
self.phone_controls_layout.setContentsMargins(10, 0, 10, 0)
|
||||||
phones_layout.addLayout(phone_controls_layout)
|
phones_layout.addLayout(self.phone_controls_layout)
|
||||||
|
|
||||||
# Setup UI for phones
|
|
||||||
for phone in self.manager.phones:
|
|
||||||
phone_container_widget, phone_display_frame, phone_button, waveform_widget, sent_waveform_widget, phone_status_label, playback_button, record_button = self._create_phone_ui(
|
|
||||||
f"Phone {phone['id']+1}", lambda checked, pid=phone['id']: self.manager.phone_action(pid, self)
|
|
||||||
)
|
|
||||||
phone['button'] = phone_button
|
|
||||||
phone['waveform'] = waveform_widget
|
|
||||||
phone['sent_waveform'] = sent_waveform_widget
|
|
||||||
phone['status_label'] = phone_status_label
|
|
||||||
phone['playback_button'] = playback_button
|
|
||||||
phone['record_button'] = record_button
|
|
||||||
|
|
||||||
# Connect audio control buttons with proper closure
|
|
||||||
playback_button.clicked.connect(lambda checked, pid=phone['id']: self.toggle_playback(pid))
|
|
||||||
record_button.clicked.connect(lambda checked, pid=phone['id']: self.toggle_recording(pid))
|
|
||||||
phone_controls_layout.addWidget(phone_container_widget)
|
|
||||||
# Connect data_received signal - it emits (data, client_id)
|
|
||||||
phone['client'].data_received.connect(lambda data, cid: self.manager.update_waveform(cid, data))
|
|
||||||
phone['client'].state_changed.connect(lambda state, num, cid=phone['id']: self.set_phone_state(cid, state, num))
|
|
||||||
phone['client'].start()
|
|
||||||
|
|
||||||
# Control buttons layout
|
# Control buttons layout
|
||||||
control_layout = QHBoxLayout()
|
control_layout = QHBoxLayout()
|
||||||
@ -264,16 +250,124 @@ class PhoneUI(QMainWindow):
|
|||||||
# Set splitter sizes
|
# Set splitter sizes
|
||||||
self.splitter.setSizes([600, 300]) # 70% phones, 30% debug
|
self.splitter.setSizes([600, 300]) # 70% phones, 30% debug
|
||||||
self.main_h_splitter.setSizes([300, 1100]) # GSM panel: 300px, rest: 1100px
|
self.main_h_splitter.setSizes([300, 1100]) # GSM panel: 300px, rest: 1100px
|
||||||
|
|
||||||
# Initialize UI
|
|
||||||
for phone in self.manager.phones:
|
|
||||||
self.update_phone_ui(phone['id'])
|
|
||||||
|
|
||||||
# Initial debug message
|
# Initial debug message
|
||||||
QTimer.singleShot(100, lambda: self.debug("DryBox UI initialized with integrated protocol"))
|
QTimer.singleShot(100, lambda: self.debug("DryBox UI initialized with integrated protocol"))
|
||||||
|
|
||||||
# Setup keyboard shortcuts
|
# Setup keyboard shortcuts
|
||||||
self.setup_shortcuts()
|
self.setup_shortcuts()
|
||||||
|
|
||||||
|
# Placeholder for manager (will be initialized after simulator starts)
|
||||||
|
self.manager = None
|
||||||
|
|
||||||
|
def initialize_phone_manager(self):
|
||||||
|
"""Initialize phone manager after GSM simulator is ready"""
|
||||||
|
self.debug("Initializing phone manager...")
|
||||||
|
self.manager = PhoneManager()
|
||||||
|
self.manager.ui = self # Set UI reference for debug logging
|
||||||
|
self.manager.initialize_phones()
|
||||||
|
|
||||||
|
# Now setup phone UIs
|
||||||
|
self.setup_phone_uis()
|
||||||
|
|
||||||
|
# Initialize UI
|
||||||
|
for phone in self.manager.phones:
|
||||||
|
self.update_phone_ui(phone['id'])
|
||||||
|
|
||||||
|
def setup_phone_uis(self):
|
||||||
|
"""Setup UI for phones after manager is initialized"""
|
||||||
|
# Find the phone controls layout
|
||||||
|
phone_controls_layout = self.phone_controls_layout
|
||||||
|
|
||||||
|
# Setup UI for phones
|
||||||
|
for phone in self.manager.phones:
|
||||||
|
phone_container_widget, phone_display_frame, phone_button, waveform_widget, sent_waveform_widget, phone_status_label, playback_button, record_button = self._create_phone_ui(
|
||||||
|
f"Phone {phone['id']+1}", lambda checked, pid=phone['id']: self.manager.phone_action(pid, self)
|
||||||
|
)
|
||||||
|
phone['button'] = phone_button
|
||||||
|
phone['waveform'] = waveform_widget
|
||||||
|
phone['sent_waveform'] = sent_waveform_widget
|
||||||
|
phone['status_label'] = phone_status_label
|
||||||
|
phone['playback_button'] = playback_button
|
||||||
|
phone['record_button'] = record_button
|
||||||
|
|
||||||
|
# Connect audio control buttons with proper closure
|
||||||
|
playback_button.clicked.connect(lambda checked, pid=phone['id']: self.toggle_playback(pid))
|
||||||
|
record_button.clicked.connect(lambda checked, pid=phone['id']: self.toggle_recording(pid))
|
||||||
|
phone_controls_layout.addWidget(phone_container_widget)
|
||||||
|
# Connect data_received signal - it emits (data, client_id)
|
||||||
|
phone['client'].data_received.connect(lambda data, cid: self.manager.update_waveform(cid, data))
|
||||||
|
phone['client'].state_changed.connect(lambda state, num, cid=phone['id']: self.set_phone_state(cid, state, num))
|
||||||
|
phone['client'].start()
|
||||||
|
|
||||||
|
def start_gsm_simulator(self):
|
||||||
|
"""Start the GSM simulator as a subprocess"""
|
||||||
|
try:
|
||||||
|
# First, try to kill any existing GSM simulator
|
||||||
|
try:
|
||||||
|
subprocess.run(['pkill', '-f', 'gsm_simulator.py'], capture_output=True)
|
||||||
|
time.sleep(0.5) # Give it time to shut down
|
||||||
|
except:
|
||||||
|
pass # Ignore if pkill fails
|
||||||
|
|
||||||
|
# Get the path to the simulator script
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
simulator_path = os.path.join(os.path.dirname(current_dir), 'simulator', 'gsm_simulator.py')
|
||||||
|
|
||||||
|
if not os.path.exists(simulator_path):
|
||||||
|
self.debug(f"ERROR: GSM simulator not found at {simulator_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Start the simulator process
|
||||||
|
self.gsm_simulator_process = subprocess.Popen(
|
||||||
|
[sys.executable, simulator_path],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
universal_newlines=True,
|
||||||
|
bufsize=1
|
||||||
|
)
|
||||||
|
|
||||||
|
# Start thread to read simulator output
|
||||||
|
simulator_thread = threading.Thread(
|
||||||
|
target=self.read_simulator_output,
|
||||||
|
daemon=True
|
||||||
|
)
|
||||||
|
simulator_thread.start()
|
||||||
|
|
||||||
|
# Give simulator time to start
|
||||||
|
time.sleep(0.5)
|
||||||
|
self.debug("GSM simulator started successfully")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.debug(f"ERROR: Failed to start GSM simulator: {e}")
|
||||||
|
|
||||||
|
def read_simulator_output(self):
|
||||||
|
"""Read output from GSM simulator subprocess"""
|
||||||
|
if self.gsm_simulator_process:
|
||||||
|
while True:
|
||||||
|
line = self.gsm_simulator_process.stdout.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
self.debug(f"[GSM Simulator] {line}")
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
stderr = self.gsm_simulator_process.stderr.read()
|
||||||
|
if stderr:
|
||||||
|
self.debug(f"[GSM Simulator ERROR] {stderr}")
|
||||||
|
|
||||||
|
def stop_gsm_simulator(self):
|
||||||
|
"""Stop the GSM simulator subprocess"""
|
||||||
|
if self.gsm_simulator_process:
|
||||||
|
self.debug("Stopping GSM simulator...")
|
||||||
|
self.gsm_simulator_process.terminate()
|
||||||
|
try:
|
||||||
|
self.gsm_simulator_process.wait(timeout=2)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
self.gsm_simulator_process.kill()
|
||||||
|
self.gsm_simulator_process = None
|
||||||
|
self.debug("GSM simulator stopped")
|
||||||
|
|
||||||
def _create_phone_ui(self, title, action_slot):
|
def _create_phone_ui(self, title, action_slot):
|
||||||
phone_container_widget = QWidget()
|
phone_container_widget = QWidget()
|
||||||
@ -409,6 +503,8 @@ class PhoneUI(QMainWindow):
|
|||||||
return phone_container_widget, phone_display_frame, phone_button, waveform_widget, sent_waveform_widget, phone_status_label, playback_button, record_button
|
return phone_container_widget, phone_display_frame, phone_button, waveform_widget, sent_waveform_widget, phone_status_label, playback_button, record_button
|
||||||
|
|
||||||
def update_phone_ui(self, phone_id):
|
def update_phone_ui(self, phone_id):
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
phone = self.manager.phones[phone_id]
|
phone = self.manager.phones[phone_id]
|
||||||
other_phone = self.manager.phones[1 - phone_id]
|
other_phone = self.manager.phones[1 - phone_id]
|
||||||
state = phone['state']
|
state = phone['state']
|
||||||
@ -484,6 +580,9 @@ class PhoneUI(QMainWindow):
|
|||||||
def set_phone_state(self, client_id, state_str, number):
|
def set_phone_state(self, client_id, state_str, number):
|
||||||
self.debug(f"Phone {client_id + 1} state change: {state_str}")
|
self.debug(f"Phone {client_id + 1} state change: {state_str}")
|
||||||
|
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
|
|
||||||
# Handle protocol-specific states
|
# Handle protocol-specific states
|
||||||
if state_str == "HANDSHAKE_COMPLETE":
|
if state_str == "HANDSHAKE_COMPLETE":
|
||||||
phone = self.manager.phones[client_id]
|
phone = self.manager.phones[client_id]
|
||||||
@ -571,7 +670,7 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def apply_gsm_settings(self):
|
def apply_gsm_settings(self):
|
||||||
"""Apply GSM settings to the phone simulation"""
|
"""Apply GSM settings to the phone simulation"""
|
||||||
if self.gsm_settings:
|
if self.gsm_settings and self.manager:
|
||||||
# Here you can apply the settings to your phone manager or simulation
|
# Here you can apply the settings to your phone manager or simulation
|
||||||
# For example, update signal quality indicators, adjust codec parameters, etc.
|
# For example, update signal quality indicators, adjust codec parameters, etc.
|
||||||
self.debug("Applying GSM settings to simulation...")
|
self.debug("Applying GSM settings to simulation...")
|
||||||
@ -654,6 +753,10 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def execute_test_step(self):
|
def execute_test_step(self):
|
||||||
"""Execute next step in test sequence"""
|
"""Execute next step in test sequence"""
|
||||||
|
if not self.manager:
|
||||||
|
self.debug("Test step skipped - manager not initialized")
|
||||||
|
return
|
||||||
|
|
||||||
phone1 = self.manager.phones[0]
|
phone1 = self.manager.phones[0]
|
||||||
phone2 = self.manager.phones[1]
|
phone2 = self.manager.phones[1]
|
||||||
|
|
||||||
@ -775,6 +878,8 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def toggle_playback(self, phone_id):
|
def toggle_playback(self, phone_id):
|
||||||
"""Toggle audio playback for a phone"""
|
"""Toggle audio playback for a phone"""
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
is_enabled = self.manager.toggle_playback(phone_id)
|
is_enabled = self.manager.toggle_playback(phone_id)
|
||||||
phone = self.manager.phones[phone_id]
|
phone = self.manager.phones[phone_id]
|
||||||
phone['playback_button'].setChecked(is_enabled)
|
phone['playback_button'].setChecked(is_enabled)
|
||||||
@ -786,6 +891,8 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def toggle_recording(self, phone_id):
|
def toggle_recording(self, phone_id):
|
||||||
"""Toggle audio recording for a phone"""
|
"""Toggle audio recording for a phone"""
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
is_recording, save_path = self.manager.toggle_recording(phone_id)
|
is_recording, save_path = self.manager.toggle_recording(phone_id)
|
||||||
phone = self.manager.phones[phone_id]
|
phone = self.manager.phones[phone_id]
|
||||||
phone['record_button'].setChecked(is_recording)
|
phone['record_button'].setChecked(is_recording)
|
||||||
@ -848,6 +955,8 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def export_audio_buffer(self, phone_id):
|
def export_audio_buffer(self, phone_id):
|
||||||
"""Export audio buffer for a phone"""
|
"""Export audio buffer for a phone"""
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
save_path = self.manager.export_buffered_audio(phone_id)
|
save_path = self.manager.export_buffered_audio(phone_id)
|
||||||
if save_path:
|
if save_path:
|
||||||
self.debug(f"Phone {phone_id + 1}: Audio buffer exported to {save_path}")
|
self.debug(f"Phone {phone_id + 1}: Audio buffer exported to {save_path}")
|
||||||
@ -856,10 +965,14 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def clear_audio_buffer(self, phone_id):
|
def clear_audio_buffer(self, phone_id):
|
||||||
"""Clear audio buffer for a phone"""
|
"""Clear audio buffer for a phone"""
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
self.manager.clear_audio_buffer(phone_id)
|
self.manager.clear_audio_buffer(phone_id)
|
||||||
|
|
||||||
def process_audio(self, phone_id, processing_type):
|
def process_audio(self, phone_id, processing_type):
|
||||||
"""Process audio with specified type"""
|
"""Process audio with specified type"""
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
save_path = self.manager.process_audio(phone_id, processing_type)
|
save_path = self.manager.process_audio(phone_id, processing_type)
|
||||||
if save_path:
|
if save_path:
|
||||||
self.debug(f"Phone {phone_id + 1}: Processed audio saved to {save_path}")
|
self.debug(f"Phone {phone_id + 1}: Processed audio saved to {save_path}")
|
||||||
@ -868,6 +981,8 @@ class PhoneUI(QMainWindow):
|
|||||||
|
|
||||||
def apply_gain_dialog(self, phone_id):
|
def apply_gain_dialog(self, phone_id):
|
||||||
"""Show dialog to get gain value"""
|
"""Show dialog to get gain value"""
|
||||||
|
if not self.manager:
|
||||||
|
return
|
||||||
gain, ok = QInputDialog.getDouble(
|
gain, ok = QInputDialog.getDouble(
|
||||||
self, "Apply Gain", "Enter gain in dB:",
|
self, "Apply Gain", "Enter gain in dB:",
|
||||||
0.0, -20.0, 20.0, 1
|
0.0, -20.0, 20.0, 1
|
||||||
@ -880,12 +995,12 @@ class PhoneUI(QMainWindow):
|
|||||||
def setup_shortcuts(self):
|
def setup_shortcuts(self):
|
||||||
"""Setup keyboard shortcuts"""
|
"""Setup keyboard shortcuts"""
|
||||||
# Phone 1 shortcuts
|
# Phone 1 shortcuts
|
||||||
QShortcut(QKeySequence("1"), self, lambda: self.manager.phone_action(0, self))
|
QShortcut(QKeySequence("1"), self, lambda: self.manager.phone_action(0, self) if self.manager else None)
|
||||||
QShortcut(QKeySequence("Ctrl+1"), self, lambda: self.toggle_playback(0))
|
QShortcut(QKeySequence("Ctrl+1"), self, lambda: self.toggle_playback(0))
|
||||||
QShortcut(QKeySequence("Alt+1"), self, lambda: self.toggle_recording(0))
|
QShortcut(QKeySequence("Alt+1"), self, lambda: self.toggle_recording(0))
|
||||||
|
|
||||||
# Phone 2 shortcuts
|
# Phone 2 shortcuts
|
||||||
QShortcut(QKeySequence("2"), self, lambda: self.manager.phone_action(1, self))
|
QShortcut(QKeySequence("2"), self, lambda: self.manager.phone_action(1, self) if self.manager else None)
|
||||||
QShortcut(QKeySequence("Ctrl+2"), self, lambda: self.toggle_playback(1))
|
QShortcut(QKeySequence("Ctrl+2"), self, lambda: self.toggle_playback(1))
|
||||||
QShortcut(QKeySequence("Alt+2"), self, lambda: self.toggle_recording(1))
|
QShortcut(QKeySequence("Alt+2"), self, lambda: self.toggle_recording(1))
|
||||||
|
|
||||||
@ -966,6 +1081,9 @@ class PhoneUI(QMainWindow):
|
|||||||
# Stop GSM simulation
|
# Stop GSM simulation
|
||||||
self.stop_gsm_simulation()
|
self.stop_gsm_simulation()
|
||||||
|
|
||||||
|
# Stop GSM simulator process
|
||||||
|
self.stop_gsm_simulator()
|
||||||
|
|
||||||
# Clean up GSM settings dialog
|
# Clean up GSM settings dialog
|
||||||
if self.gsm_settings_dialog is not None:
|
if self.gsm_settings_dialog is not None:
|
||||||
self.gsm_settings_dialog.close()
|
self.gsm_settings_dialog.close()
|
||||||
@ -973,12 +1091,13 @@ class PhoneUI(QMainWindow):
|
|||||||
self.gsm_settings_dialog = None
|
self.gsm_settings_dialog = None
|
||||||
|
|
||||||
# Clean up audio player
|
# Clean up audio player
|
||||||
if hasattr(self.manager, 'audio_player'):
|
if self.manager and hasattr(self.manager, 'audio_player'):
|
||||||
self.manager.audio_player.cleanup()
|
self.manager.audio_player.cleanup()
|
||||||
|
|
||||||
# Stop all phone clients
|
# Stop all phone clients
|
||||||
for phone in self.manager.phones:
|
if self.manager:
|
||||||
phone['client'].stop()
|
for phone in self.manager.phones:
|
||||||
|
phone['client'].stop()
|
||||||
|
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Run DryBox UI with proper Wayland support on Fedora
|
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
|
||||||
# Use native Wayland if available
|
|
||||||
export QT_QPA_PLATFORM=wayland
|
|
||||||
|
|
||||||
# Run the UI
|
|
||||||
cd UI
|
|
||||||
python3 main.py
|
|
@ -1,14 +0,0 @@
|
|||||||
# Use official Python image
|
|
||||||
FROM python:3.9-slim
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy the simulator script
|
|
||||||
COPY gsm_simulator.py .
|
|
||||||
|
|
||||||
# Expose the port
|
|
||||||
EXPOSE 12345
|
|
||||||
|
|
||||||
# Run the simulator
|
|
||||||
CMD ["python", "gsm_simulator.py"]
|
|
@ -1,68 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Script to launch the GSM Simulator in Docker
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
IMAGE_NAME="gsm-simulator"
|
|
||||||
CONTAINER_NAME="gsm-sim"
|
|
||||||
PORT="12345"
|
|
||||||
LOG_FILE="gsm_simulator.log"
|
|
||||||
|
|
||||||
# Check if Docker is installed
|
|
||||||
if ! command -v docker &> /dev/null; then
|
|
||||||
echo "Error: Docker is not installed. Please install Docker and try again."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if gsm_simulator.py exists
|
|
||||||
if [ ! -f "gsm_simulator.py" ]; then
|
|
||||||
echo "Error: gsm_simulator.py not found in the current directory."
|
|
||||||
echo "Please ensure gsm_simulator.py is present and try again."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create Dockerfile if it doesn't exist
|
|
||||||
if [ ! -f "Dockerfile" ]; then
|
|
||||||
echo "Creating Dockerfile..."
|
|
||||||
cat <<EOF > Dockerfile
|
|
||||||
FROM python:3.9-slim
|
|
||||||
WORKDIR /app
|
|
||||||
COPY gsm_simulator.py .
|
|
||||||
EXPOSE 12345
|
|
||||||
CMD ["python", "gsm_simulator.py"]
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure log file is writable
|
|
||||||
touch $LOG_FILE
|
|
||||||
chmod 666 $LOG_FILE
|
|
||||||
|
|
||||||
# Build the Docker image
|
|
||||||
echo "Building Docker image: $IMAGE_NAME..."
|
|
||||||
docker build -t $IMAGE_NAME .
|
|
||||||
|
|
||||||
# Check if the build was successful
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Error: Failed to build Docker image."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stop and remove any existing container
|
|
||||||
if [ "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then
|
|
||||||
echo "Stopping existing container: $CONTAINER_NAME..."
|
|
||||||
docker stop $CONTAINER_NAME
|
|
||||||
fi
|
|
||||||
if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then
|
|
||||||
echo "Removing existing container: $CONTAINER_NAME..."
|
|
||||||
docker rm $CONTAINER_NAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean up dangling images
|
|
||||||
docker image prune -f
|
|
||||||
|
|
||||||
# Run the Docker container interactively
|
|
||||||
echo "Launching GSM Simulator in Docker container: $CONTAINER_NAME..."
|
|
||||||
docker run -it --rm -p $PORT:$PORT --name $CONTAINER_NAME $IMAGE_NAME | tee $LOG_FILE
|
|
||||||
|
|
||||||
# Note: Script will block here until container exits
|
|
||||||
echo "GSM Simulator stopped. Logs saved to $LOG_FILE."
|
|
Loading…
Reference in New Issue
Block a user