go test (no udp limit)
This commit is contained in:
parent
76626278ca
commit
581d0ce7ae
149
scripts/go/README.md
Normal file
149
scripts/go/README.md
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
# Go UDP Receiver - High-Performance UDP Testing Tool
|
||||||
|
|
||||||
|
A high-performance Go implementation for testing UDP reception performance. This version focuses purely on UDP performance without any GUI/display components, making it ideal for benchmarking network throughput and detecting dropped frames.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Pure UDP Performance**: No GUI overhead, focuses on network performance
|
||||||
|
- **High Throughput**: Go's native performance handles 200+ fps easily
|
||||||
|
- **Low Latency**: Dedicated goroutine for UDP reception
|
||||||
|
- **Drop Detection**: Tracks and reports dropped frames based on timing
|
||||||
|
- **Bandwidth Monitoring**: Real-time bandwidth usage statistics
|
||||||
|
- **Clean Output**: Console-based statistics ideal for logging and analysis
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Go 1.21+**: Download from https://go.dev/dl/
|
||||||
|
- **No other dependencies**: Pure Go implementation using only standard library
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
### Windows (PowerShell)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# From project root
|
||||||
|
.\scripts\build_go_receiver.ps1
|
||||||
|
|
||||||
|
# Or manually from scripts/go directory
|
||||||
|
cd scripts\go
|
||||||
|
go build -o recv_raw_rolling.exe main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux/macOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
./scripts/build_go_receiver.sh
|
||||||
|
|
||||||
|
# Or manually from scripts/go directory
|
||||||
|
cd scripts/go
|
||||||
|
go build -o recv_raw_rolling main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Windows
|
||||||
|
cd scripts\go
|
||||||
|
.\recv_raw_rolling.exe
|
||||||
|
|
||||||
|
# Linux/macOS
|
||||||
|
cd scripts/go
|
||||||
|
./recv_raw_rolling
|
||||||
|
```
|
||||||
|
|
||||||
|
Press `Ctrl+C` to stop and display final statistics.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit the constants in [`main.go`](main.go:13) to match your setup:
|
||||||
|
|
||||||
|
- `COLUMN_WIDTH`: Width of incoming data (default: 4)
|
||||||
|
- `COLUMN_HEIGHT`: Height of incoming data (default: 2456)
|
||||||
|
- `CHANNELS`: Number of color channels (default: 3 for RGB)
|
||||||
|
- `UDP_PORT`: UDP port to listen on (default: 5000)
|
||||||
|
- `EXPECTED_FPS`: Expected frame rate for drop detection (default: 200)
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
The receiver displays statistics every 100 frames:
|
||||||
|
|
||||||
|
```
|
||||||
|
Frame 100: Real FPS: 198.5 | Instant: 200.2 | BW: 34.56 MB/s
|
||||||
|
Frame 200: Real FPS: 199.1 | Instant: 199.8 | BW: 34.62 MB/s
|
||||||
|
Frame 300: Real FPS: 199.8 | Instant: 200.1 | BW: 34.59 MB/s | ⚠️ 2 drops
|
||||||
|
```
|
||||||
|
|
||||||
|
On shutdown (Ctrl+C), it displays final statistics:
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Final Statistics ===
|
||||||
|
Total Frames: 1234
|
||||||
|
Total Time: 6.17 seconds
|
||||||
|
Average FPS: 200.0
|
||||||
|
Instant FPS: 199.8
|
||||||
|
Total Drops: 5
|
||||||
|
Total Data: 35.67 MB
|
||||||
|
Bandwidth: 34.58 MB/s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Comparison
|
||||||
|
|
||||||
|
| Implementation | Typical FPS | CPU Usage | Overhead |
|
||||||
|
|---------------|-------------|-----------|----------|
|
||||||
|
| Python (OpenCV) | ~100 fps | High | Display rendering |
|
||||||
|
| **Go (Console)** | **200+ fps** | **Minimal** | **None** |
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
- **UDP Performance Testing**: Measure maximum UDP throughput
|
||||||
|
- **Network Diagnostics**: Detect packet loss and timing issues
|
||||||
|
- **Benchmarking**: Compare network setups and configurations
|
||||||
|
- **Automated Testing**: Console output suitable for CI/CD pipelines
|
||||||
|
- **High-Speed Data Acquisition**: Validate 200+ fps data streams
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- **Concurrent UDP Reception**: Dedicated goroutine prevents frame drops
|
||||||
|
- **16MB Receive Buffer**: Configured for high-throughput scenarios
|
||||||
|
- **Zero Display Overhead**: Pure console output for maximum performance
|
||||||
|
- **Graceful Shutdown**: Handles Ctrl+C and displays final statistics
|
||||||
|
- **No External Dependencies**: Pure Go standard library
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Build Errors
|
||||||
|
|
||||||
|
Ensure you have Go 1.21+ installed:
|
||||||
|
```bash
|
||||||
|
go version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Already in Use
|
||||||
|
|
||||||
|
If port 5000 is in use, edit `UDP_PORT` in [`main.go`](main.go:31) and rebuild.
|
||||||
|
|
||||||
|
### High Drop Rate
|
||||||
|
|
||||||
|
1. Check network configuration with [`scripts/udp_optimize.ps1`](../udp_optimize.ps1) (Windows)
|
||||||
|
2. Verify sender is transmitting at correct rate
|
||||||
|
3. Check for network congestion or interference
|
||||||
|
4. Consider increasing receive buffer size in code
|
||||||
|
|
||||||
|
## Comparison with Python Version
|
||||||
|
|
||||||
|
The Python version ([`scripts/recv_raw_rolling.py`](../recv_raw_rolling.py)) includes visual display but has lower throughput:
|
||||||
|
|
||||||
|
- **Use Go version for**: Performance testing, benchmarking, headless servers
|
||||||
|
- **Use Python version for**: Visual debugging, development, seeing actual data
|
||||||
|
|
||||||
|
## Changes from Previous Version
|
||||||
|
|
||||||
|
This version has been simplified to focus purely on UDP performance:
|
||||||
|
|
||||||
|
- ✅ Removed SDL2 dependency
|
||||||
|
- ✅ Removed GUI/display components
|
||||||
|
- ✅ Added comprehensive statistics
|
||||||
|
- ✅ Improved drop detection
|
||||||
|
- ✅ Added bandwidth monitoring
|
||||||
|
- ✅ Zero external dependencies
|
||||||
3
scripts/go/go.mod
Normal file
3
scripts/go/go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module recv_raw_rolling
|
||||||
|
|
||||||
|
go 1.21
|
||||||
0
scripts/go/go.sum
Normal file
0
scripts/go/go.sum
Normal file
203
scripts/go/main.go
Normal file
203
scripts/go/main.go
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Stream parameters (match your GStreamer sender)
|
||||||
|
COLUMN_WIDTH = 4
|
||||||
|
COLUMN_HEIGHT = 2456
|
||||||
|
CHANNELS = 3
|
||||||
|
FRAME_SIZE = COLUMN_WIDTH * COLUMN_HEIGHT * CHANNELS // 29472 bytes
|
||||||
|
|
||||||
|
// Line drop detection parameters
|
||||||
|
EXPECTED_FPS = 200
|
||||||
|
EXPECTED_INTERVAL_MS = 1000.0 / EXPECTED_FPS // 5ms for 200fps
|
||||||
|
DROP_THRESHOLD_MS = EXPECTED_INTERVAL_MS * 2.5
|
||||||
|
STATS_WINDOW_SIZE = 100
|
||||||
|
STATUS_INTERVAL = 100
|
||||||
|
|
||||||
|
UDP_PORT = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
type FrameStats struct {
|
||||||
|
intervals []float64
|
||||||
|
totalDrops int
|
||||||
|
dropsSince int
|
||||||
|
frameCount int
|
||||||
|
firstFrameTime time.Time
|
||||||
|
lastFrameTime time.Time
|
||||||
|
totalBytes int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFrameStats() *FrameStats {
|
||||||
|
return &FrameStats{
|
||||||
|
intervals: make([]float64, 0, STATS_WINDOW_SIZE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FrameStats) addInterval(intervalMs float64) {
|
||||||
|
if len(fs.intervals) >= STATS_WINDOW_SIZE {
|
||||||
|
fs.intervals = fs.intervals[1:]
|
||||||
|
}
|
||||||
|
fs.intervals = append(fs.intervals, intervalMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FrameStats) avgInterval() float64 {
|
||||||
|
if len(fs.intervals) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
sum := 0.0
|
||||||
|
for _, v := range fs.intervals {
|
||||||
|
sum += v
|
||||||
|
}
|
||||||
|
return sum / float64(len(fs.intervals))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FrameStats) printSummary() {
|
||||||
|
if fs.frameCount == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elapsed := time.Since(fs.firstFrameTime).Seconds()
|
||||||
|
avgFPS := float64(fs.frameCount) / elapsed
|
||||||
|
avgInterval := fs.avgInterval()
|
||||||
|
instantFPS := 0.0
|
||||||
|
if avgInterval > 0 {
|
||||||
|
instantFPS = 1000.0 / avgInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
bandwidth := float64(fs.totalBytes) / elapsed / (1024 * 1024) // MB/s
|
||||||
|
|
||||||
|
fmt.Println("\n=== Final Statistics ===")
|
||||||
|
fmt.Printf("Total Frames: %d\n", fs.frameCount)
|
||||||
|
fmt.Printf("Total Time: %.2f seconds\n", elapsed)
|
||||||
|
fmt.Printf("Average FPS: %.2f\n", avgFPS)
|
||||||
|
fmt.Printf("Instant FPS: %.2f\n", instantFPS)
|
||||||
|
fmt.Printf("Total Drops: %d\n", fs.totalDrops)
|
||||||
|
fmt.Printf("Total Data: %.2f MB\n", float64(fs.totalBytes)/(1024*1024))
|
||||||
|
fmt.Printf("Bandwidth: %.2f MB/s\n", bandwidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Setup UDP socket
|
||||||
|
addr := net.UDPAddr{
|
||||||
|
Port: UDP_PORT,
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
}
|
||||||
|
conn, err := net.ListenUDP("udp", &addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("UDP listen failed:", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Set receive buffer size to 16MB
|
||||||
|
if err := conn.SetReadBuffer(16 * 1024 * 1024); err != nil {
|
||||||
|
log.Printf("Warning: Failed to set recv buffer: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("High-Performance UDP Receiver for Raw %dx%d RGB columns\n", COLUMN_WIDTH, COLUMN_HEIGHT)
|
||||||
|
fmt.Printf("Listening on UDP port %d\n", UDP_PORT)
|
||||||
|
fmt.Printf("Expected frame size: %d bytes\n", FRAME_SIZE)
|
||||||
|
fmt.Printf("Press Ctrl+C to stop\n\n")
|
||||||
|
|
||||||
|
stats := newFrameStats()
|
||||||
|
buffer := make([]byte, 65536)
|
||||||
|
|
||||||
|
// Handle graceful shutdown
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
// Channel for shutdown
|
||||||
|
done := make(chan bool)
|
||||||
|
|
||||||
|
// Start UDP receiver goroutine
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
n, _, err := conn.ReadFromUDP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
log.Printf("UDP read error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != FRAME_SIZE {
|
||||||
|
// Wrong packet size, skip
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTime := time.Now()
|
||||||
|
|
||||||
|
// Initialize timing on first frame
|
||||||
|
if stats.firstFrameTime.IsZero() {
|
||||||
|
stats.firstFrameTime = currentTime
|
||||||
|
fmt.Println("Started receiving frames...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line drop detection
|
||||||
|
if !stats.lastFrameTime.IsZero() {
|
||||||
|
intervalMs := float64(currentTime.Sub(stats.lastFrameTime).Microseconds()) / 1000.0
|
||||||
|
stats.addInterval(intervalMs)
|
||||||
|
|
||||||
|
if intervalMs > DROP_THRESHOLD_MS {
|
||||||
|
stats.totalDrops++
|
||||||
|
stats.dropsSince++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.lastFrameTime = currentTime
|
||||||
|
stats.frameCount++
|
||||||
|
stats.totalBytes += int64(n)
|
||||||
|
|
||||||
|
// Print status
|
||||||
|
if stats.frameCount%STATUS_INTERVAL == 0 {
|
||||||
|
elapsed := currentTime.Sub(stats.firstFrameTime).Seconds()
|
||||||
|
realFPS := float64(stats.frameCount) / elapsed
|
||||||
|
avgInterval := stats.avgInterval()
|
||||||
|
instantFPS := 0.0
|
||||||
|
if avgInterval > 0 {
|
||||||
|
instantFPS = 1000.0 / avgInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
bandwidth := float64(stats.totalBytes) / elapsed / (1024 * 1024) // MB/s
|
||||||
|
|
||||||
|
status := fmt.Sprintf("Frame %6d: Real FPS: %6.1f | Instant: %6.1f | BW: %6.2f MB/s",
|
||||||
|
stats.frameCount, realFPS, instantFPS, bandwidth)
|
||||||
|
if stats.dropsSince > 0 {
|
||||||
|
status += fmt.Sprintf(" | ⚠️ %d drops", stats.dropsSince)
|
||||||
|
stats.dropsSince = 0
|
||||||
|
} else if stats.totalDrops > 0 {
|
||||||
|
status += fmt.Sprintf(" | Total drops: %d", stats.totalDrops)
|
||||||
|
}
|
||||||
|
fmt.Println(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for shutdown signal
|
||||||
|
<-sigChan
|
||||||
|
fmt.Println("\nShutdown signal received...")
|
||||||
|
close(done)
|
||||||
|
|
||||||
|
// Give a moment for cleanup
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// Print final statistics
|
||||||
|
stats.printSummary()
|
||||||
|
fmt.Println("Goodbye!")
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user