very good very nice, C++ working now! so you can test the app with a ESP32's serial port
All checks were successful
Maven Build / build (push) Successful in 10m13s
All checks were successful
Maven Build / build (push) Successful in 10m13s
This commit is contained in:
@@ -1,10 +1,201 @@
|
||||
#include <WiFi.h>
|
||||
|
||||
// Mock dyno simulation parameters
|
||||
const int SERIAL_BAUD_RATE = 9600;
|
||||
const int DATA_SEND_INTERVAL_MS = 100; // Send data every 100ms
|
||||
const int SIMULATION_STEP_MS = 50; // Update simulation every 50ms
|
||||
|
||||
// Simulation state
|
||||
enum SimulationState {
|
||||
IDLE,
|
||||
ACCELERATING,
|
||||
HOLDING,
|
||||
DECELERATING
|
||||
};
|
||||
|
||||
SimulationState currentState = IDLE;
|
||||
unsigned long lastDataSend = 0;
|
||||
unsigned long lastSimulationUpdate = 0;
|
||||
unsigned long stateStartTime = 0;
|
||||
|
||||
// RPM simulation variables
|
||||
float currentRPM = 0.0;
|
||||
float targetRPM = 0.0;
|
||||
float rpmAcceleration = 0.0;
|
||||
float maxRPM = 11000.0;
|
||||
float minRPM = 800.0; // Idle RPM
|
||||
float idleRPM = 800.0;
|
||||
|
||||
// Dyno run parameters
|
||||
const float ACCELERATION_RATE = 25.0; // RPM per 100ms during acceleration
|
||||
const float DECELERATION_RATE = 30.0; // RPM per 100ms during deceleration
|
||||
const int HOLD_DURATION_MS = 2000; // Hold at max RPM for 2 seconds
|
||||
const int IDLE_DURATION_MS = 3000; // Stay at idle for 3 seconds
|
||||
|
||||
// Add some realistic noise and variations
|
||||
float rpmNoise = 0.0;
|
||||
int noiseCounter = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600); // Ensure this matches the baud rate in Main.java
|
||||
Serial.begin(SERIAL_BAUD_RATE);
|
||||
|
||||
// Initialize random seed
|
||||
randomSeed(analogRead(0));
|
||||
|
||||
// Wait for serial to initialize
|
||||
delay(2000);
|
||||
|
||||
Serial.println("ESP32 Mock Dyno Data Generator");
|
||||
Serial.println("Sending RPM data for dyno simulation...");
|
||||
Serial.println("==================================");
|
||||
|
||||
// Start at idle
|
||||
currentRPM = idleRPM;
|
||||
targetRPM = idleRPM;
|
||||
currentState = IDLE;
|
||||
stateStartTime = millis();
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Replace this with your actual RPM measurement logic
|
||||
double currentRPM = 1500.0; // Example RPM value
|
||||
Serial.println(currentRPM);
|
||||
delay(100); // Send data every 100ms, adjust as needed
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// Update simulation state
|
||||
if (currentTime - lastSimulationUpdate >= SIMULATION_STEP_MS) {
|
||||
updateSimulation();
|
||||
lastSimulationUpdate = currentTime;
|
||||
}
|
||||
|
||||
// Send data to Java application
|
||||
if (currentTime - lastDataSend >= DATA_SEND_INTERVAL_MS) {
|
||||
sendRPMData();
|
||||
lastDataSend = currentTime;
|
||||
}
|
||||
|
||||
delay(10); // Small delay to prevent excessive CPU usage
|
||||
}
|
||||
|
||||
void updateSimulation() {
|
||||
unsigned long currentTime = millis();
|
||||
unsigned long stateElapsed = currentTime - stateStartTime;
|
||||
|
||||
switch (currentState) {
|
||||
case IDLE:
|
||||
// Stay at idle RPM for a while, then start accelerating
|
||||
targetRPM = idleRPM + random(-50, 51); // Add some idle variation
|
||||
if (stateElapsed > IDLE_DURATION_MS) {
|
||||
currentState = ACCELERATING;
|
||||
targetRPM = maxRPM;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Starting dyno run - ACCELERATING");
|
||||
}
|
||||
break;
|
||||
|
||||
case ACCELERATING:
|
||||
// Accelerate towards max RPM
|
||||
if (currentRPM < maxRPM - 100) {
|
||||
currentRPM += ACCELERATION_RATE * (SIMULATION_STEP_MS / 100.0);
|
||||
// Add some realistic acceleration curve (not perfectly linear)
|
||||
float accelerationFactor = 1.0 - (currentRPM / maxRPM) * 0.3;
|
||||
currentRPM = currentRPM * accelerationFactor + currentRPM * (1.0 - accelerationFactor);
|
||||
} else {
|
||||
currentState = HOLDING;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Reached max RPM - HOLDING");
|
||||
}
|
||||
break;
|
||||
|
||||
case HOLDING:
|
||||
// Hold at max RPM with some variation
|
||||
targetRPM = maxRPM + random(-100, 101);
|
||||
if (stateElapsed > HOLD_DURATION_MS) {
|
||||
currentState = DECELERATING;
|
||||
targetRPM = idleRPM;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Starting deceleration - DECELERATING");
|
||||
}
|
||||
break;
|
||||
|
||||
case DECELERATING:
|
||||
// Decelerate back to idle
|
||||
if (currentRPM > idleRPM + 100) {
|
||||
currentRPM -= DECELERATION_RATE * (SIMULATION_STEP_MS / 100.0);
|
||||
// Add engine braking effect (faster deceleration at higher RPM)
|
||||
float brakingFactor = 1.0 + (currentRPM / maxRPM) * 0.5;
|
||||
currentRPM -= (DECELERATION_RATE * brakingFactor - DECELERATION_RATE) * (SIMULATION_STEP_MS / 100.0);
|
||||
} else {
|
||||
currentRPM = idleRPM;
|
||||
currentState = IDLE;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Returned to idle - IDLE");
|
||||
Serial.println("==================================");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Add realistic noise and fluctuations
|
||||
addRealisticNoise();
|
||||
|
||||
// Ensure RPM doesn't go below 0 or above reasonable limits
|
||||
currentRPM = constrain(currentRPM, 0, maxRPM + 500);
|
||||
}
|
||||
|
||||
void addRealisticNoise() {
|
||||
// Add different types of noise based on RPM range
|
||||
float baseNoise = random(-10, 11); // Basic noise
|
||||
|
||||
// Engine vibration increases with RPM
|
||||
float vibrationNoise = (currentRPM / 1000.0) * random(-5, 6);
|
||||
|
||||
// Periodic fluctuations (simulating engine cycles)
|
||||
noiseCounter++;
|
||||
float periodicNoise = sin(noiseCounter * 0.1) * (currentRPM / 2000.0) * 3.0;
|
||||
|
||||
// Combine all noise sources
|
||||
rpmNoise = baseNoise + vibrationNoise + periodicNoise;
|
||||
|
||||
// Apply noise with some smoothing
|
||||
currentRPM += rpmNoise * 0.3; // Reduce noise impact
|
||||
}
|
||||
|
||||
void sendRPMData() {
|
||||
// Send just the RPM value as your Java code expects
|
||||
Serial.println(currentRPM, 0); // Send as integer (no decimal places)
|
||||
|
||||
// Optional: Send debug info to Serial Monitor (comment out for production)
|
||||
// Serial.print("DEBUG - State: ");
|
||||
// Serial.print(getStateString());
|
||||
// Serial.print(", RPM: ");
|
||||
// Serial.print(currentRPM, 1);
|
||||
// Serial.print(", Noise: ");
|
||||
// Serial.println(rpmNoise, 1);
|
||||
}
|
||||
|
||||
String getStateString() {
|
||||
switch (currentState) {
|
||||
case IDLE: return "IDLE";
|
||||
case ACCELERATING: return "ACCELERATING";
|
||||
case HOLDING: return "HOLDING";
|
||||
case DECELERATING: return "DECELERATING";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// Function to manually trigger a dyno run (call this if you want manual control)
|
||||
void triggerDynoRun() {
|
||||
if (currentState == IDLE) {
|
||||
currentState = ACCELERATING;
|
||||
stateStartTime = millis();
|
||||
Serial.println("Manual dyno run triggered!");
|
||||
}
|
||||
}
|
||||
|
||||
// Function to reset to idle (emergency stop)
|
||||
void resetToIdle() {
|
||||
currentState = IDLE;
|
||||
currentRPM = idleRPM;
|
||||
targetRPM = idleRPM;
|
||||
stateStartTime = millis();
|
||||
Serial.println("Reset to idle!");
|
||||
}
|
||||
@@ -29,66 +29,28 @@ public class Main {
|
||||
private static LeistungBerechner leistungBerechner;
|
||||
private static long lastTimestamp = 0;
|
||||
private static double lastOmega = 0.0;
|
||||
private static final double WHEEL_RADIUS_METERS = 0.25; // Example: 25 cm radius for the dyno wheel
|
||||
private static final double WHEEL_RADIUS_METERS = 0.25;
|
||||
|
||||
private static boolean testMode = false;
|
||||
private static ScheduledExecutorService testDataExecutor;
|
||||
private static int testRpmCounter = 1000;
|
||||
private static final double[] TEST_RPM_VALUES = {
|
||||
0, 500, 1000, 1500, 2000, 2500, 3000, 3500,
|
||||
4000, 4500, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8500,
|
||||
9000, 9500, 10000, 10500, 11000, 11500, 11000, 10500, 10000, 9500,
|
||||
9000, 8500, 8000, 7500, 7000, 6500, 6000, 5500, 5000, 4500,
|
||||
4000, 3500, 3000, 2500, 2000, 1500, 1000, 500, 0
|
||||
};
|
||||
|
||||
private static int testRpmCurrentIndex = 0;
|
||||
private static double testRpmCurrentValue = 0.0;
|
||||
private static final double TEST_RPM_INCREMENT_STEP = 100.0; // Increased for faster simulation
|
||||
|
||||
private static java.util.List<Double> smoothRpmData;
|
||||
private static int smoothRpmDataIndex = 0;
|
||||
private static final double TEST_SIMULATION_DELTA_TIME = 0.05; // 50 milliseconds
|
||||
|
||||
private static TreeMap<Double, Double> simulatedPowerCurve;
|
||||
private static TreeMap<Double, Double> simulatedTorqueCurve;
|
||||
private static SerialPort currentSerialPort;
|
||||
private static ScheduledExecutorService serialExecutor;
|
||||
private static BufferedReader serialReader;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Initialize simulated curves
|
||||
simulatedPowerCurve = new TreeMap<>();
|
||||
simulatedPowerCurve.put(0.0, 0.0);
|
||||
simulatedPowerCurve.put(4200.0, 2.0);
|
||||
simulatedPowerCurve.put(6000.0, 5.0);
|
||||
simulatedPowerCurve.put(7000.0, 6.0);
|
||||
simulatedPowerCurve.put(8000.0, 7.5);
|
||||
simulatedPowerCurve.put(8945.0, 8.5); // Peak Power
|
||||
simulatedPowerCurve.put(9500.0, 8.0);
|
||||
simulatedPowerCurve.put(10500.0, 7.0);
|
||||
simulatedPowerCurve.put(11400.0, 6.5);
|
||||
simulatedPowerCurve.put(12000.0, 6.0);
|
||||
|
||||
simulatedTorqueCurve = new TreeMap<>();
|
||||
simulatedTorqueCurve.put(0.0, 0.0);
|
||||
simulatedTorqueCurve.put(4200.0, 3.0);
|
||||
simulatedTorqueCurve.put(5000.0, 4.0);
|
||||
simulatedTorqueCurve.put(6000.0, 5.0);
|
||||
simulatedTorqueCurve.put(8379.0, 6.8); // Peak Torque
|
||||
simulatedTorqueCurve.put(9000.0, 6.0);
|
||||
simulatedTorqueCurve.put(10000.0, 5.0);
|
||||
simulatedTorqueCurve.put(11400.0, 4.0);
|
||||
simulatedTorqueCurve.put(12000.0, 3.5);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI = new DynoGUI();
|
||||
leistungBerechner = new LeistungBerechner();
|
||||
addControlPanel();
|
||||
// Optionally start in test mode for development
|
||||
// toggleTestMode();
|
||||
startSerialPortListener(); // Start listening for serial data immediately
|
||||
});
|
||||
}
|
||||
|
||||
private static void addControlPanel() {
|
||||
JPanel controlPanel = new JPanel();
|
||||
|
||||
JButton toggleModeButton = new JButton("Toggle Test Mode");
|
||||
toggleModeButton.addActionListener(e -> toggleTestMode());
|
||||
controlPanel.add(toggleModeButton);
|
||||
@@ -105,11 +67,20 @@ public class Main {
|
||||
resetChartButton.addActionListener(e -> dynoGUI.resetChart());
|
||||
controlPanel.add(resetChartButton);
|
||||
|
||||
JButton reconnectButton = new JButton("Reconnect Serial");
|
||||
reconnectButton.addActionListener(e -> reconnectSerial());
|
||||
controlPanel.add(reconnectButton);
|
||||
|
||||
dynoGUI.add(controlPanel, BorderLayout.SOUTH);
|
||||
dynoGUI.revalidate();
|
||||
dynoGUI.repaint();
|
||||
}
|
||||
|
||||
private static void reconnectSerial() {
|
||||
stopSerialPortListener();
|
||||
startSerialPortListener();
|
||||
}
|
||||
|
||||
private static void toggleTestMode() {
|
||||
testMode = !testMode;
|
||||
if (testMode) {
|
||||
@@ -133,69 +104,38 @@ public class Main {
|
||||
}
|
||||
|
||||
dynoGUI.resetChart();
|
||||
lastTimestamp = 0; // Reset for a new run
|
||||
lastTimestamp = 0;
|
||||
lastOmega = 0.0;
|
||||
testRpmCurrentIndex = 0;
|
||||
testRpmCurrentValue = TEST_RPM_VALUES[0]; // Start at the first RPM value
|
||||
|
||||
// Pre-generate smooth RPM data
|
||||
smoothRpmData = new ArrayList<>();
|
||||
if (TEST_RPM_VALUES.length > 0) {
|
||||
smoothRpmData.add(TEST_RPM_VALUES[0]); // Start with the first point
|
||||
}
|
||||
|
||||
for (int i = 0; i < TEST_RPM_VALUES.length - 1; i++) {
|
||||
double startRpm = TEST_RPM_VALUES[i];
|
||||
double endRpm = TEST_RPM_VALUES[i+1];
|
||||
|
||||
if (startRpm == endRpm) {
|
||||
// If start and end RPM are the same, just add it if it's not already the last element
|
||||
if (smoothRpmData.isEmpty() || smoothRpmData.get(smoothRpmData.size() - 1).doubleValue() != endRpm) {
|
||||
smoothRpmData.add(endRpm);
|
||||
}
|
||||
continue; // Move to the next segment
|
||||
}
|
||||
|
||||
// Determine the direction of change
|
||||
int direction = (endRpm > startRpm) ? 1 : -1;
|
||||
double currentRpm = startRpm;
|
||||
|
||||
// Generate intermediate points
|
||||
while (true) {
|
||||
double nextRpm = currentRpm + direction * TEST_RPM_INCREMENT_STEP;
|
||||
|
||||
// Check if we would overshoot the endRpm
|
||||
if ((direction > 0 && nextRpm > endRpm) || (direction < 0 && nextRpm < endRpm)) {
|
||||
// Add the exact endRpm if it's not already the last one
|
||||
if (smoothRpmData.isEmpty() || smoothRpmData.get(smoothRpmData.size() - 1).doubleValue() != endRpm) {
|
||||
smoothRpmData.add(endRpm);
|
||||
}
|
||||
break; // Segment finished
|
||||
} else {
|
||||
smoothRpmData.add(nextRpm);
|
||||
currentRpm = nextRpm;
|
||||
}
|
||||
}
|
||||
}
|
||||
smoothRpmDataIndex = 0;
|
||||
|
||||
testDataExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
testDataExecutor.scheduleAtFixedRate(() -> {
|
||||
simulateSmoothRpmRun();
|
||||
}, 0, 1, TimeUnit.MILLISECONDS); // Update RPM every 10ms for faster animation
|
||||
}
|
||||
// Simulate a realistic dyno run pattern
|
||||
long currentTime = System.currentTimeMillis();
|
||||
double elapsedSeconds = (currentTime - lastTimestamp) / 1000.0;
|
||||
|
||||
private static void simulateSmoothRpmRun() {
|
||||
if (smoothRpmDataIndex >= smoothRpmData.size()) {
|
||||
stopTestRun();
|
||||
JOptionPane.showMessageDialog(null, "Test run finished.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
showPrintableChart();
|
||||
return;
|
||||
}
|
||||
// Simulate acceleration, hold, then deceleration
|
||||
double simulatedRpm;
|
||||
if (elapsedSeconds < 5.0) {
|
||||
// Acceleration phase (0-5 seconds)
|
||||
simulatedRpm = 800 + (11000 - 800) * (elapsedSeconds / 5.0);
|
||||
} else if (elapsedSeconds < 7.0) {
|
||||
// Hold at max RPM (5-7 seconds)
|
||||
simulatedRpm = 11000;
|
||||
} else if (elapsedSeconds < 12.0) {
|
||||
// Deceleration phase (7-12 seconds)
|
||||
simulatedRpm = 11000 - (11000 - 800) * ((elapsedSeconds - 7.0) / 5.0);
|
||||
} else {
|
||||
// Return to idle after 12 seconds
|
||||
simulatedRpm = 800;
|
||||
stopTestRun();
|
||||
}
|
||||
|
||||
double currentSimulatedRpm = smoothRpmData.get(smoothRpmDataIndex);
|
||||
processRPMData(currentSimulatedRpm, TEST_SIMULATION_DELTA_TIME);
|
||||
smoothRpmDataIndex++;
|
||||
// Add some realistic noise
|
||||
simulatedRpm += (Math.random() * 200 - 100);
|
||||
simulatedRpm = Math.max(0, simulatedRpm);
|
||||
|
||||
processRPMData(simulatedRpm, 0.05); // Use 50ms delta time for test mode
|
||||
}, 0, 50, TimeUnit.MILLISECONDS); // Update every 50ms
|
||||
}
|
||||
|
||||
private static void stopTestRun() {
|
||||
@@ -208,9 +148,6 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
private static SerialPort currentSerialPort;
|
||||
private static ScheduledExecutorService serialExecutor;
|
||||
|
||||
private static void startSerialPortListener() {
|
||||
SerialPort[] comPorts = SerialPort.getCommPorts();
|
||||
if (comPorts.length == 0) {
|
||||
@@ -250,23 +187,29 @@ public class Main {
|
||||
return;
|
||||
}
|
||||
|
||||
final SerialPort finalChosenPort = chosenPort;
|
||||
currentSerialPort = finalChosenPort; // Store for later closing
|
||||
currentSerialPort = chosenPort;
|
||||
chosenPort.setBaudRate(9600);
|
||||
chosenPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
|
||||
|
||||
finalChosenPort.setBaudRate(9600); // Common baud rate, can be adjusted
|
||||
if (finalChosenPort.openPort()) {
|
||||
System.out.println("Serial port " + finalChosenPort.getSystemPortName() + " opened successfully.");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(finalChosenPort.getInputStream()));
|
||||
if (chosenPort.openPort()) {
|
||||
System.out.println("Serial port " + chosenPort.getSystemPortName() + " opened successfully.");
|
||||
serialReader = new BufferedReader(new InputStreamReader(chosenPort.getInputStream()));
|
||||
|
||||
serialExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
serialExecutor.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
if (reader.ready()) {
|
||||
String line = reader.readLine();
|
||||
if (serialReader.ready()) {
|
||||
String line = serialReader.readLine();
|
||||
if (line != null && !line.trim().isEmpty()) {
|
||||
try {
|
||||
double rpm = Double.parseDouble(line.trim());
|
||||
processRPMData(rpm); // Call the main processRPMData for live data
|
||||
// Handle potential incomplete lines or multiple values
|
||||
String[] values = line.trim().split("[\\s,]+");
|
||||
for (String value : values) {
|
||||
if (!value.isEmpty()) {
|
||||
double rpm = Double.parseDouble(value);
|
||||
processRPMData(rpm); // Process each valid RPM value
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("Received non-numeric data: " + line);
|
||||
}
|
||||
@@ -276,124 +219,93 @@ public class Main {
|
||||
System.err.println("Error reading from serial port: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
stopSerialPortListener();
|
||||
JOptionPane.showMessageDialog(null, "Serial port error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Serial port error: " + e.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
}, 0, 100, TimeUnit.MILLISECONDS); // Read every 100ms
|
||||
}, 0, 10, TimeUnit.MILLISECONDS); // Check more frequently for data
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Failed to open serial port " + finalChosenPort.getSystemPortName(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Failed to open serial port " + chosenPort.getSystemPortName(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void stopSerialPortListener() {
|
||||
if (serialExecutor != null) {
|
||||
serialExecutor.shutdownNow();
|
||||
try {
|
||||
if (!serialExecutor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
|
||||
serialExecutor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
serialExecutor.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
serialExecutor = null;
|
||||
}
|
||||
|
||||
if (serialReader != null) {
|
||||
try {
|
||||
serialReader.close();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error closing serial reader: " + e.getMessage());
|
||||
}
|
||||
serialReader = null;
|
||||
}
|
||||
|
||||
if (currentSerialPort != null && currentSerialPort.isOpen()) {
|
||||
currentSerialPort.closePort();
|
||||
System.out.println("Serial port " + currentSerialPort.getSystemPortName() + " closed.");
|
||||
currentSerialPort = null;
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
||||
});
|
||||
}
|
||||
|
||||
// Overloaded processRPMData for live mode, calculates deltaZeit from system time
|
||||
private static void processRPMData(double rpm) {
|
||||
processRPMData(rpm, 0.0); // Pass 0.0 to indicate no forced delta time, use system time
|
||||
}
|
||||
|
||||
// Main processRPMData for both live and test mode, uses forcedDeltaTime if provided
|
||||
private static void processRPMData(double rpm, double forcedDeltaTime) {
|
||||
long currentTimestamp = System.currentTimeMillis();
|
||||
|
||||
double effectiveDeltaZeit;
|
||||
double leistung;
|
||||
double drehmoment;
|
||||
|
||||
if (forcedDeltaTime > 0) {
|
||||
// Test mode: Use simulated curves with linear interpolation
|
||||
effectiveDeltaZeit = forcedDeltaTime;
|
||||
|
||||
leistung = interpolate(simulatedPowerCurve, rpm);
|
||||
drehmoment = interpolate(simulatedTorqueCurve, rpm);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
});
|
||||
|
||||
// For test mode, we still need to update lastOmega for the next iteration
|
||||
// in case the next simulated RPM is used for an actual calculation (e.g. if we switch modes).
|
||||
lastOmega = rpmToOmega(rpm);
|
||||
lastTimestamp = currentTimestamp; // Also update timestamp for consistency, though not used in test mode calculation
|
||||
return;
|
||||
|
||||
} else {
|
||||
// Live mode: Calculate from actual sensor data
|
||||
if (lastTimestamp == 0) {
|
||||
lastTimestamp = currentTimestamp;
|
||||
lastOmega = rpmToOmega(rpm);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, rpm); // Update RPM, but no power/torque yet for the first point
|
||||
});
|
||||
return; // Skip calculations for the very first data point
|
||||
}
|
||||
effectiveDeltaZeit = (currentTimestamp - lastTimestamp) / 1000.0;
|
||||
// Ensure effectiveDeltaZeit is always positive and non-zero for live mode
|
||||
if (effectiveDeltaZeit <= 0) {
|
||||
effectiveDeltaZeit = 0.001; // Assign a small positive value to avoid division by zero
|
||||
}
|
||||
|
||||
double currentOmega = rpmToOmega(rpm);
|
||||
|
||||
// If there's no change in RPM, then acceleration is zero. Display zero power/torque.
|
||||
if (currentOmega == lastOmega) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, rpm); // Update RPM, but output zero power/torque
|
||||
});
|
||||
lastTimestamp = currentTimestamp; // Crucially update for the next iteration
|
||||
lastOmega = currentOmega; // Crucially update for the next iteration
|
||||
return;
|
||||
}
|
||||
|
||||
drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, effectiveDeltaZeit);
|
||||
leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
});
|
||||
|
||||
if (lastTimestamp == 0) {
|
||||
lastTimestamp = currentTimestamp;
|
||||
lastOmega = currentOmega;
|
||||
lastOmega = rpmToOmega(rpm);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, rpm);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
double deltaTime = (currentTimestamp - lastTimestamp) / 1000.0;
|
||||
if (deltaTime <= 0) deltaTime = 0.001;
|
||||
|
||||
double currentOmega = rpmToOmega(rpm);
|
||||
double drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, deltaTime);
|
||||
double leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
});
|
||||
|
||||
lastTimestamp = currentTimestamp;
|
||||
lastOmega = currentOmega;
|
||||
}
|
||||
|
||||
// Linear interpolation method
|
||||
private static double interpolate(TreeMap<Double, Double> curve, double rpm) {
|
||||
if (curve.containsKey(rpm)) {
|
||||
return curve.get(rpm);
|
||||
}
|
||||
Map.Entry<Double, Double> lower = curve.floorEntry(rpm);
|
||||
Map.Entry<Double, Double> upper = curve.ceilingEntry(rpm);
|
||||
private static void processRPMData(double rpm, double forcedDeltaTime) {
|
||||
// For test mode with forced delta time
|
||||
double currentOmega = rpmToOmega(rpm);
|
||||
double drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, forcedDeltaTime);
|
||||
double leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||
|
||||
if (lower == null) {
|
||||
return upper.getValue(); // RPM is below lowest defined point, return lowest point's value
|
||||
}
|
||||
if (upper == null) {
|
||||
return lower.getValue(); // RPM is above highest defined point, return highest point's value
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
});
|
||||
|
||||
double x1 = lower.getKey();
|
||||
double y1 = lower.getValue();
|
||||
double x2 = upper.getKey();
|
||||
double y2 = upper.getValue();
|
||||
|
||||
// Linear interpolation formula: y = y1 + ((y2 - y1) / (x2 - x1)) * (x - x1)
|
||||
if (x2 == x1) { // Avoid division by zero if two points have the same RPM (shouldn't happen with proper data)
|
||||
return y1;
|
||||
}
|
||||
return y1 + ((y2 - y1) / (x2 - x1)) * (rpm - x1);
|
||||
lastOmega = currentOmega;
|
||||
lastTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static double rpmToOmega(double rpm) {
|
||||
@@ -418,7 +330,9 @@ public class Main {
|
||||
try {
|
||||
job.print();
|
||||
} catch (PrinterException ex) {
|
||||
JOptionPane.showMessageDialog(chartFrame, "Printing error: " + ex.getMessage(), "Print Error", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(chartFrame,
|
||||
"Printing error: " + ex.getMessage(),
|
||||
"Print Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -451,27 +365,30 @@ public class Main {
|
||||
File finalFile = new File(filePath);
|
||||
|
||||
try {
|
||||
// Use the dimensions of the ChartPanel to create the PDF document
|
||||
Document document = new Document(new Rectangle(chartPanel.getWidth(), chartPanel.getHeight()));
|
||||
PdfWriter.getInstance(document, new FileOutputStream(finalFile));
|
||||
document.open();
|
||||
|
||||
// Create BufferedImage from the ChartPanel's chart
|
||||
java.awt.Image awtImage = chartPanel.getChart().createBufferedImage(chartPanel.getWidth(), chartPanel.getHeight());
|
||||
java.awt.Image awtImage = chartPanel.getChart().createBufferedImage(
|
||||
chartPanel.getWidth(), chartPanel.getHeight());
|
||||
Image pdfImage = Image.getInstance(awtImage, null);
|
||||
|
||||
// Scale image to fit document
|
||||
pdfImage.scaleToFit(document.getPageSize().getWidth() - 20, document.getPageSize().getHeight() - 20); // Add some padding
|
||||
pdfImage.scaleToFit(document.getPageSize().getWidth() - 20,
|
||||
document.getPageSize().getHeight() - 20);
|
||||
pdfImage.setAlignment(Image.ALIGN_CENTER);
|
||||
|
||||
document.add(pdfImage);
|
||||
document.close();
|
||||
|
||||
JOptionPane.showMessageDialog(null, "Chart saved as PDF successfully!\n" + finalFile.getAbsolutePath(), "Save Successful", JOptionPane.INFORMATION_MESSAGE);
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Chart saved as PDF successfully!\n" + finalFile.getAbsolutePath(),
|
||||
"Save Successful", JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(null, "Error saving chart as PDF: " + ex.getMessage(), "Save Error", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Error saving chart as PDF: " + ex.getMessage(),
|
||||
"Save Error", JOptionPane.ERROR_MESSAGE);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user