"""
Dynamic Field Calibration Simulation
Simulates 7 days of field data for sensor calibration
"""

import numpy as np
import json
import time

print("="*60)
print("FIELD CALIBRATION SIMULATION")
print("7-day realistic field data generation")
print("="*60)

# Sensor properties from lab calibration
sensors = {
    1: {"name": "Sensor_1", "offset": -0.960, "alpha": 0.001594, "gain": 1.0981},
    2: {"name": "Sensor_2", "offset": -0.680, "alpha": 0.001566, "gain": 1.0650},
    3: {"name": "Sensor_3", "offset": -0.520, "alpha": 0.001551, "gain": 1.0510},
    4: {"name": "Sensor_4", "offset": 0.700, "alpha": 0.001430, "gain": 0.9280}
}

K_SENSOR = 90.91  # Physical conversion constant
DAYS = 7
HOURS_PER_DAY = 24
TOTAL_HOURS = DAYS * HOURS_PER_DAY

# Generate realistic field and temperature scenarios
print("\n1. Generating realistic environmental conditions...")

# True electric field variations (snowstorm patterns)
np.random.seed(42)  # For reproducibility
time_hours = np.linspace(0, TOTAL_HOURS, TOTAL_HOURS)

# Base field with daily variations
true_field = 1000 + 200 * np.sin(2 * np.pi * time_hours / 24)

# Add snowstorm events
storm_indices = []
for i in range(10):
    start = np.random.uniform(10, TOTAL_HOURS - 10)
    duration = np.random.uniform(2, 6)
    
    # Bell-shaped storm event
    x = np.linspace(-3, 3, int(duration))
    bell = 500 * np.exp(-x**2)
    
    start_idx = int(start)
    if start_idx + len(bell) < len(true_field):
        true_field[start_idx:start_idx+len(bell)] += bell
        storm_indices.append(start_idx + len(bell)//2)

# Temperature variations (realistic for snowstorm conditions)
temperature = -10 + 15 * np.sin(2 * np.pi * time_hours / 24) + np.random.normal(0, 2, TOTAL_HOURS)

print(f"   • Time period: {DAYS} days ({TOTAL_HOURS} hours)")
print(f"   • Electric field range: {np.min(true_field):.0f} to {np.max(true_field):.0f} V/m")
print(f"   • Temperature range: {np.min(temperature):.0f} to {np.max(temperature):.0f} °C")
print(f"   • Snowstorm events simulated: {len(storm_indices)}")

# Simulate sensor readings
print("\n2. Simulating sensor readings with lab-calibrated parameters...")

sensor_readings = {}
calibration_data = {}

for sensor_id, props in sensors.items():
    readings = []
    
    for hour in range(TOTAL_HOURS):
        T = temperature[hour]
        E_true = true_field[hour]
        
        # Calculate current from true field
        I_true = E_true / K_SENSOR
        
        # Apply sensor-specific gain and offset
        I_measured = (I_true / props["gain"]) - props["offset"]
        
        # Apply temperature compensation
        I_compensated = I_measured * (1 + props["alpha"] * (T - 20))
        
        # Convert back to electric field
        E_measured = I_compensated * K_SENSOR * props["gain"]
        
        # Calculate error
        error_percent = 100 * (E_measured - E_true) / E_true
        
        readings.append({
            "hour": hour,
            "time_days": hour / 24,
            "temperature_C": float(T),
            "true_field_Vm": float(E_true),
            "measured_field_Vm": float(E_measured),
            "error_percent": float(error_percent)
        })
    
    sensor_readings[sensor_id] = readings
    
    # Calculate statistics
    errors = [r["error_percent"] for r in readings]
    calibration_data[sensor_id] = {
        "name": props["name"],
        "max_error": float(np.max(errors)),
        "min_error": float(np.min(errors)),
        "mean_error": float(np.mean(errors)),
        "std_error": float(np.std(errors))
    }

print("\n3. Sensor Performance Summary (before field calibration):")
print("   " + "-"*50)
for sensor_id in sensors.keys():
    stats = calibration_data[sensor_id]
    print(f"   {stats['name']}:")
    print(f"     • Mean error: {stats['mean_error']:6.2f}%")
    print(f"     • Error range: {stats['min_error']:6.2f}% to {stats['max_error']:6.2f}%")
    print(f"     • Std deviation: {stats['std_error']:6.2f}%")

# Field calibration procedure
print("\n4. Field Calibration Procedure (using storm events):")
print("   " + "-"*50)

# Use storm events for calibration
if storm_indices:
    print(f"   Using {len(storm_indices)} storm events for calibration")
    
    # Collect readings during storms
    storm_readings = {sensor_id: [] for sensor_id in sensors.keys()}
    
    for event_idx in storm_indices:
        for sensor_id in sensors.keys():
            reading = sensor_readings[sensor_id][event_idx]["measured_field_Vm"]
            storm_readings[sensor_id].append(reading)
    
    # Calculate median readings during storms
    median_readings = {}
    for sensor_id, readings in storm_readings.items():
        median_readings[sensor_id] = np.median(readings)
    
    # Find reference sensor (closest to median of medians)
    all_medians = list(median_readings.values())
    ref_value = np.median(all_medians)
    
    ref_sensor = None
    min_diff = float('inf')
    for sensor_id, median in median_readings.items():
        diff = abs(median - ref_value)
        if diff < min_diff:
            min_diff = diff
            ref_sensor = sensor_id
    
    # Calculate calibration factors
    calibration_factors = {}
    ref_reading = median_readings[ref_sensor]
    
    for sensor_id, median in median_readings.items():
        if sensor_id == ref_sensor:
            calibration_factors[sensor_id] = 1.0
        else:
            calibration_factors[sensor_id] = ref_reading / median
    
    print(f"\n   Reference sensor: {sensors[ref_sensor]['name']}")
    print("\n   Calibration factors calculated:")
    for sensor_id, factor in calibration_factors.items():
        print(f"   {sensors[sensor_id]['name']}: {factor:.4f}")
    
    # Apply calibration
    print("\n5. Applying field calibration factors...")
    
    calibrated_errors = {}
    for sensor_id in sensors.keys():
        factor = calibration_factors[sensor_id]
        original_errors = [r["error_percent"] for r in sensor_readings[sensor_id]]
        
        # Simulate calibrated readings
        calibrated_errors[sensor_id] = []
        for reading in sensor_readings[sensor_id]:
            calibrated_field = reading["measured_field_Vm"] * factor
            true_field = reading["true_field_Vm"]
            calibrated_error = 100 * (calibrated_field - true_field) / true_field
            calibrated_errors[sensor_id].append(calibrated_error)
    
    # Calculate post-calibration statistics
    print("\n6. Performance after field calibration:")
    print("   " + "-"*50)
    
    for sensor_id in sensors.keys():
        orig_stats = calibration_data[sensor_id]
        cal_errors = calibrated_errors[sensor_id]
        
        mean_error_cal = np.mean(cal_errors)
        std_error_cal = np.std(cal_errors)
        
        improvement = 100 * (1 - abs(mean_error_cal) / abs(orig_stats["mean_error"]))
        
        print(f"   {sensors[sensor_id]['name']}:")
        print(f"     • Before: mean error = {orig_stats['mean_error']:6.2f}%")
        print(f"     • After:  mean error = {mean_error_cal:6.2f}%")
        print(f"     • Improvement: {improvement:.1f}%")
        print(f"     • Post-calibration std: {std_error_cal:.2f}%")

# Save results
print("\n7. Saving simulation results...")

results = {
    "simulation_date": time.strftime("%Y-%m-%d %H:%M:%S"),
    "duration_days": DAYS,
    "sensors": sensors,
    "calibration_data": calibration_data,
    "calibration_factors": calibration_factors if 'calibration_factors' in locals() else None,
    "storm_events_used": len(storm_indices),
    "summary": {
        "pre_calibration_max_error": max([d["max_error"] for d in calibration_data.values()]),
        "pre_calibration_mean_error": np.mean([d["mean_error"] for d in calibration_data.values()]),
        "reference_sensor": sensors[ref_sensor]["name"] if 'ref_sensor' in locals() else None
    }
}

with open('field_simulation_results.json', 'w') as f:
    json.dump(results, f, indent=2)

print(f"\n✅ Simulation completed successfully!")
print(f"📁 Results saved to: field_simulation_results.json")
print(f"📊 Data points generated: {TOTAL_HOURS} hours × 4 sensors = {TOTAL_HOURS * 4} readings")
print("\n" + "="*60)
print("NEXT STEPS:")
print("1. Review field_simulation_results.json")
print("2. Use calibration factors in your C code")
print("3. Deploy sensors and collect real field data")
print("4. Compare simulated vs real calibration factors")
print("="*60)
PYCODE

echo ""
echo "🚀 Simulation script created: field_calibration_sim.py"
echo "📊 To run: python field_calibration_sim.py"
