#!/usr/bin/python3
import numpy as np
import scipy.io.wavfile as wav
from scipy.signal import hilbert

def load_and_demodulate(wav_path):
    """Lädt die WAV-Datei und berechnet die Hüllkurve des Signals."""
    sample_rate, data = wav.read(wav_path)
    
    # Falls Stereo, in Mono umwandeln
    if len(data.shape) > 1:
        data = data[:, 0]
        
    # Normalisieren
    data = data.astype(np.float32) / np.max(np.abs(data))
    
    # Hüllkurve mittels Analytischem Signal (Hilbert-Transformation) bestimmen
    # Falls das Signal bereits ein reines AM-Demodulations-Audio ist, reicht np.abs(data)
    envelope = np.abs(hilbert(data))
    
    # Tiefpass/Glättung über ein gleitendes Fenster (z.B. 10ms)
    window_len = int(sample_rate * 0.01)
    envelope_smooth = np.convolve(envelope, np.ones(window_len)/window_len, mode='same')
    
    return sample_rate, envelope_smooth

def binarize_signal(envelope, sample_rate):
    """Wandelt die Hüllkurve über einen adaptiven Schwellenwert in High/Low um."""
    # MSF schaltet auf 0% ab. Ein Schwellenwert bei 35% des Maximums ist meist robust gegen Rauschen.
    threshold = np.percentile(envelope, 35)
    binary = (envelope > threshold).astype(np.int8)
    return binary

def extract_pulses(binary, sample_rate):
    """Findet alle Flankenwechsel und berechnet die Pulsdauern in Millisekunden."""
    # Wechsel finden (1 zu 0 oder 0 zu 1)
    diff = np.diff(binary)
    edges = np.where(diff != 0)[0]
    
    pulses = []
    # Wir messen die Dauer der Absenkungen (Träger AUS = 0)
    for i in range(len(edges) - 1):
        idx = edges[i]
        # Wenn das Signal auf 0 abfällt
        if binary[idx] == 1 and binary[idx+1] == 0:
            duration_ms = (edges[i+1] - idx) / sample_rate * 1000
            start_ms = idx / sample_rate * 1000
            pulses.append((start_ms, duration_ms))
            
    return pulses

def decode_msf(pulses):
    """Parst die Pulsliste basierend auf dem MSF-Timing."""
    print(f"Starte Dekodierung von {len(pulses)} erkannten Pulsen...\n")
    
    current_minute = []
    in_sync = False
    
    # Hilfsfunktion zum Zuordnen der Pulsdauer mit Toleranz
    def get_pulse_type(dur):
        if 70 <= dur <= 140:   return 100  # 100 ms Bit
        if 170 <= dur <= 240:  return 200  # 200 ms Bit
        if 450 <= dur <= 550:  return 500  # 500 ms Minutenmarker
        return None

    for i in range(len(pulses) - 1):
        start, duration = pulses[i]
        p_type = get_pulse_type(duration)
        
        if p_type == 500:
            print(f"\n--- [Minutenstart erkannt bei {start/1000:.2f}s] ---")
            if in_sync and len(current_minute) > 0:
                # Hier könnte man die gesammelten Bits der Vor-Minute auswerten
                print(f"Bits der letzten Minute: {current_minute}")
            current_minute = []
            in_sync = True
            continue
            
        if in_sync and p_type is not None:
            # Zeitabstand zum nächsten Puls berechnen, um Bit A und B zu trennen
            next_start = pulses[i+1][0]
            gap = next_start - start
            
            # Wenn der nächste Puls innerhalb der gleichen Sekunde liegt (~300ms Abstand)
            # handelt es sich um eine Bit-A / Bit-B Sequenz.
            if 250 <= gap <= 350:
                p_type_b = get_pulse_type(pulses[i+1][1])
                bit_a = 1 if p_type == 200 else 0
                bit_b = 1 if p_type_b == 100 else 0
                current_minute.append((bit_a, bit_b))
                # Überspringe den nächsten Puls, da als Bit B verarbeitet
                i += 1 
            else:
                # Nur Bit A vorhanden (kein zweiter Puls bei 300ms)
                bit_a = 1 if p_type == 200 else 0
                current_minute.append((bit_a, 0))

# --- Hauptprogramm ---
if __name__ == "__main__":
    # Pfad zu deiner vom WebSDR heruntergeladenen WAV-Datei eintragen
    wav_filename = "websdr_recording_60khz.wav" 
    
    try:
        sr, env = load_and_demodulate(wav_filename)
        bin_sig = binarize_signal(env, sr)
        detected_pulses = extract_pulses(bin_sig, sr)
        decode_msf(detected_pulses)
    except FileNotFoundError:
        print(f"Datei {wav_filename} nicht gefunden. Bitte Pfad anpassen.")
