- System Overview
- Architecture & Components
- gcn_lstm.py - GCN-LSTM Signal Prediction
- SVMRegression.py - Vehicle Position Prediction
- Data Collection & Flow
- C++ Integration
- File Formats & Paths
- Build & Execution
- Testing & Validation
- Troubleshooting
Simu5G is an OMNeT++/INET-based 5G/LTE network simulator with a proactive handover system that uses machine learning to predict network conditions and make intelligent handover decisions before signal quality degrades.
Ensemble Prediction Approach:
- GCN-LSTM predicts: Signal Quality (RSSI), Base Station Load, Distance
- SVMRegression predicts: Vehicle position (X, Y coordinates)
- Ensemble Decision: All three conditions must be favorable to trigger handover
if ((rssi >= predictedRSSI) && (load <= predictedLoad) && (dist <= predictedDist))
→ Trigger handover to candidate base station
- OMNeT++ 6.0: Event-driven network simulator core
- INET 4.4.0: Network protocol stack (MAC/RLC/PDCP/IP layers)
- Python 3: Machine learning models
- PyTorch + torch_geometric: GCN-LSTM implementation
- scikit-learn: SVMRegression implementation
- Veins/SUMO: Vehicular mobility simulation
- X2 Interface: Inter-base-station handover coordination
File: src/stack/phy/layer/LtePhyUe.cc#L510-L620
Function: handoverHandler()
Triggered: Every broadcast message (≈1ms intervals)
Collects:
- RSSI: Received signal strength indicator (from channel model)
- Distance: Vehicle distance to serving/candidate towers (from channel model)
- Tower Load: Base station load based on connected vehicles (calculated)
- Speed: Vehicle velocity (from mobility model)
- Position: Vehicle X,Y coordinates (from mobility model)
Primary Storage: src/Datafiles/dataStorage.csv
- Format:
Time,VehicleID,TowerID,RSSI,Distance,TowerLoad - Updated every broadcast (≈1ms)
- Used by: gcn_lstm.py
Secondary Storage: src/Datafiles/simulator_data.csv
- Format:
Time,vehicleId,TowerID,RSSI,Distance,X,Y - Updated every broadcast (≈1ms)
- Used by: SVMRegression.py
t=10s, t=20s, t=30s, ... → Execute gcn_lstm.py
Input: dataStorage.csv (all collected metrics)
Output: Three predicted values (parsed from stdout)
t=15s, t=30s, t=45s, ... → Execute SVMRegression.py <vehicleID> <futureTime>
Input: simulator_data.csv (position data only)
Output: Predicted X,Y coordinates
Decision Logic LtePhyUe.cc#L644:
if ((rssi >= predictedRSSI) && (eachTowerLoad <= predictedTowerLoad) && (dist <= predictedDistance))
{
// Trigger handover
}Predicts three network metrics using a hybrid GCN-LSTM architecture:
- Tower Load: Base station congestion level (0-1)
- RSSI: Received signal strength indicator (dB)
- Distance: Vehicle distance to tower (meters)
Location: src/stack/phy/layer/LtePhyUe.cc#L619-L624
if ((int)simTime().dbl() % 10 == 0 && !isRunPythonScript)
{
callingPython(filePath_LtePhyUe+"python_script/gcn_lstm.py");
clearHalfFileData(filePath_LtePhyUe+"dataStorage.csv");
isRunPythonScript = true;
}Frequency: Every 10 simulation seconds
File: dataStorage.csv
Format:
Time,VehicleID,TowerID,RSSI,Distance,TowerLoad
10.0,2087,1,95.3,500.2,0.45
10.01,2087,2,94.8,505.1,0.50
Columns Required:
- TowerLoad (Col 5): Node feature in GCN
- RSSI (Col 3): Edge weight in GCN
- Distance (Col 4): Edge weight in GCN
Architecture:
-
Data Loading & Normalization
features = scaler.fit_transform(df[['TowerLoad', 'RSSI', 'Distance']].values)
-
Graph Construction
- Nodes: One per unique tower ID
- Edges: Between towers with recorded data
- Node Features: TowerLoad
- Edge Weights: RSSI and Distance
-
GCN Layer - Learns graph structure and node relationships
-
LSTM Layer - Learns temporal patterns
-
Training: 25 epochs with Adam optimizer, MSE loss
Format (three print statements):
Final Predicted Tower Load: 0.47
Final Predicted RSSI: 92.5
Final Predicted Distance: 512.1
Parsing LtePhyUe.cc#L424-L444:
- Uses
sscanf()to extract double values - Updates global variables:
predictedTowerLoad,predictedRSSI,predictedDistance
Original (❌ Would Fail):
df = pd.read_csv('/home/guest/Downloads/.../dataStorage.csv')Updated (✅ Fixed):
script_dir = os.path.dirname(os.path.abspath(__file__))
data_path = os.path.join(script_dir, '..', 'dataStorage.csv')
df = pd.read_csv(data_path, sep=",")pandas>=1.0.0
torch>=1.9.0
torch-geometric>=2.0.0
scikit-learn>=0.24.0
numpy>=1.19.0
Predicts future vehicle X,Y coordinates using Support Vector Regression to enable distance-based handover decisions.
Location: src/stack/phy/layer/LtePhyUe.cc#L473-L476
void LtePhyUe::runSVR(unsigned short vehicleID, int simTime)
{
std::string pypredSVR_CmdPyCpp = "python3 " + filePath_LtePhyUe +
"python_script/SVMRegression.py " + std::to_string(vehicleID) +
" " + std::to_string(simTime);
system(pypredSVR_CmdPyCpp.c_str());
}Frequency: Every 15 simulation seconds
Command Example:
python3 /path/to/python_script/SVMRegression.py 2087 20File: simulator_data.csv
Format:
Time,vehicleId,TowerID,RSSI,Distance,X,Y
10.0,2087,1,95.3,500.2,1000.45,2000.75
10.01,2087,2,94.8,505.1,1001.20,2001.30
Columns Required:
Time(chronological ordering and delta computation)X,Y(absolute UE coordinates used both as history context and prediction targets)vehicleId(row filter so each SVR instance trains per vehicle)
- Load & Filter: Read
simulator_data.csv, then slice rows wherevehicleId == <UE>. - History Guard: Require at least
HISTORY_LEN + 1samples (defaultHISTORY_LEN = 5, configurable via theSVR_HISTORY_LENenvironment variable). Until that threshold is met the script outputs0 0, so the C++ layer falls back to instantaneous distance. - Feature Construction:
- Slide a window of
HISTORY_LENrows acrossvehicle_data. - For each window, flatten the last
HISTORY_LEN(Time, X, Y)triples and append their first-order differences(Δt, Δx, Δy)computed directly from those rows. This captures both position and instantaneous velocity without introducing new logged metrics.
- Slide a window of
- Training: Stack all window vectors into a matrix, scale with
StandardScaler, and fitMultiOutputRegressor(SVR(kernel='rbf'))to predict the next(X, Y)sample following each window. - Inference: Take the most recent
HISTORY_LENrows, build the same feature vector, replace the final time entry withpredict_At(future horizon), scale, and runpredict()to obtain the next(X, Y)estimate.
File: outputSVR.txt
Format (two space-separated floats):
1010.45 2005.20
Fallbacks:
- Insufficient history (
len(vehicle_data) < HISTORY_LEN + 1): script writes0 0soLtePhyUereuses the instantaneous distance fromdist.txt. - No rows for vehicle: same
0 0output.
Parsing LtePhyUe.cc#L481-L488:
std::pair<double, double> LtePhyUe::getParfromFileForSVR(...)
{
file >> xCoord >> yCoord; // Read two space-separated doubles
return std::make_pair(xCoord, yCoord);
}Function LtePhyUe.cc#L489-L501:
double LtePhyUe::calculatePredictedDistance(double predX, double predY, const inet::Coord& towerCoord)
{
double dx = predX - towerCoord.x;
double dy = predY - towerCoord.y;
return sqrt(dx * dx + dy * dy); // Euclidean distance
}Fallback Chain SVMRegression.py#L16-L28:
script_dir = os.path.dirname(os.path.abspath(__file__))
datafiles_dir = os.path.dirname(script_dir)
simulator_data_path = os.path.join(datafiles_dir, 'simulator_data.csv')
# Note: Removed fallback to dataStorage.csv because it lacks X,Y columns
# dataStorage.csv only has: Time,VehicleID,TowerID,RSSI,Distance,TowerLoad
# SVMRegression needs columns [5,6] which don't exist in dataStorage.csvnumpy>=1.19.0
pandas>=1.0.0
scikit-learn>=0.24.0
Every broadcast (≈1ms):
→ LtePhyUe::handoverHandler() called
→ RSSI, distance, tower load collected
→ writeToCSV(dataStorage.csv) [for gcn_lstm.py]
→ writeVehiclePositionToCSV(...) [for SVMRegression.py]
→ GCN-LSTM prediction triggered
→ dataStorage.csv has ~14,400 rows (100KB)
→ Model trains and predicts
→ Predictions: TowerLoad, RSSI, Distance
→ CSV reduced to half size: 7,200 rows
→ runSVR(vehicleID, 20) called
→ SVMRegression.py executed
→ Position predicted at t=20
→ outputSVR.txt written
→ Distance calculated using tower coordinates
For each broadcast:
→ Check: (rssi >= predictedRSSI) && (load <= predictedLoad) && (dist <= predictedDist)
→ If all true: handover triggered
→ X2 procedure executed
→ Metrics tracked
| File | Location | Format | Updated | Usage |
|---|---|---|---|---|
| dataStorage.csv | Datafiles/ | CSV | Every ~1ms | gcn_lstm.py input |
| simulator_data.csv | Datafiles/ | CSV | Every ~1ms | SVMRegression.py input |
| outputSVR.txt | Datafiles/python_script/ | Text (2 nums) | Every 15s | Predicted X,Y |
writeToCSV()[LtePhyUe.cc#L320-L340]: Appends to dataStorage.csvwriteVehiclePositionToCSV()[LtePhyUe.cc#L354-L373]: Appends to simulator_data.csv
callingPython()[LtePhyUe.cc#L419-L446]: Executes gcn_lstm.py, parses outputrunSVR()[LtePhyUe.cc#L473-L476]: Executes SVMRegression.py with arguments
getParfromFileForSVR()[LtePhyUe.cc#L481-L488]: Reads outputSVR.txtcalculatePredictedDistance()[LtePhyUe.cc#L489-L501]: Euclidean distanceclearHalfFileData()[LtePhyUe.cc#L373-L410]: CSV size managementcalculateEachTowerLoad()[LtePhyUe.cc#L285-L309]: Base station loadgetDoubleValueFile()[LtePhyUe.cc#L311-L319]: Read metric files
handoverHandler() [LtePhyUe.cc#L510-L680]
Central function that:
- Processes broadcast messages
- Collects network metrics
- Updates CSV files
- Calls prediction scripts (periodically)
- Makes handover decisions
- Triggers handover execution
Time,VehicleID,TowerID,RSSI,Distance,TowerLoad
10.0,2087,1,95.3,500.2,0.45
- Used by: gcn_lstm.py
- Size: ~1MB per hour
Time,vehicleId,TowerID,RSSI,Distance,X,Y
10.0,2087,1,95.3,500.2,1000.45,2000.75
- Used by: SVMRegression.py
- Size: ~2MB per hour
std::string filePath_LtePhyUe =
"/home/guest/Downloads/Predictive-Mobility-Modeling-Handover-Decision-Making/Project_GCN_LSTM_HO/simu5G/src/Datafiles/";script_dir = os.path.dirname(os.path.abspath(__file__))
data_path = os.path.join(script_dir, '..', 'dataStorage.csv')✅ Dynamic: Works on any machine
script_dir = os.path.dirname(os.path.abspath(__file__))
datafiles_dir = os.path.dirname(script_dir)
simulator_data_path = os.path.join(datafiles_dir, 'simulator_data.csv')✅ Dynamic: Works on any machine
# From project root
. setenv # Source environment
# Generate makefiles (one-time)
make makefiles
# Build release version
make
# Build debug version
make MODE=debug
# Clean artifacts
make clean# Navigate to simulation directory
cd simulations/NR/ProactiveHO
# Execute
./runcd src/Datafiles/python_script
# Create test dataStorage.csv with sample data
# Run the script
python3 gcn_lstm.py
# Verify output printed to consolecd src/Datafiles/python_script
# Create simulator_data.csv with sample data
python3 SVMRegression.py 2087 160
# Check outputSVR.txt
cat outputSVR.txt- Handover Count: Reasonable for scenario
- Failed Handovers: < 5%
- Ping-Pong Rate: < 10% of HOs
- Packet Loss: Minimal during handover
pip3 install torch torch-geometric scikit-learn pandas- Verify simulation running (≥15 seconds for data)
- Check CSV exists:
ls -la src/Datafiles/dataStorage.csv
Replace hardcoded path in LtePhyUe.h with relative or environment variable approach
Verify clearHalfFileData() called (line 623 in handoverHandler)
- Test Python script manually
- Check output format matches sscanf pattern
- Verify dataStorage.csv has data
-
GCN-LSTM (every 10s): Network quality prediction
- Input: dataStorage.csv
- Output: TowerLoad, RSSI, Distance predictions
-
SVMRegression (every 15s): Vehicle position prediction
- Input: simulator_data.csv
- Output: Predicted X,Y coordinates
-
Ensemble Handover: All conditions required
- RSSI ≥ predicted, Load ≤ predicted, Distance ≤ predicted
-
Data Files: Auto-generated by C++
- dataStorage.csv (network metrics)
- simulator_data.csv (vehicle positions)
-
Paths: ✅ Python scripts portable,
⚠️ C++ hardcoded (needs fix)