(*DCF77 Decoding Mathematica Program*)
(*1. Importiere das Audio-File und filtere das 750 Hz Signal*)
audio=Import[NotebookDirectory[]<>"DCF77_Test_File_WebSDR_heute.wav","WAV"];
samplingRate=7119;
threshold=0.23;
thresholdMinutes=0.55;
filAudio=BandpassFilter[audio,{Quantity[700,"Hz"],Quantity[800,"Hz"]},samplingRate];

(*2. Extrahiere die Audiodaten und flache die Liste ab*)
filteredAudioData=Flatten[AudioData[filAudio]];

(*3. AM-Demodulation durch Absolutwertbildung*)
envelope=Abs[filteredAudioData];

(*4. Glättung durch Tiefpassfilter 10 Hz*)
demodulatedSignal=LowpassFilter[envelope,2*10/samplingRate];

(*Normalisiere die demodulierte Hüllkurve*)
normalizedEnvelope=demodulatedSignal/Max[demodulatedSignal];

(*5. Visualisierung der normalisierten Hüllkurve und Thresholds*)
startSeconds=1;endSeconds=180;lenSeconds=endSeconds-startSeconds;
ListLinePlot[{Table[0.1,{i,1,60*samplingRate}],Table[threshold,{i,1,lenSeconds*samplingRate}],Table[thresholdMinutes,{i,1,lenSeconds*samplingRate}],normalizedEnvelope[[startSeconds*samplingRate;;Min[endSeconds*samplingRate,Length[normalizedEnvelope]]]]},PlotStyle->{Green,Red,Orange,Blue},PlotLegends->{"1 Minute","Threshold: "<>ToString[threshold],"ThresholdMinutes: "<>ToString[thresholdMinutes],"demod. Envelope"},PlotRange->All,PlotLabel->"Normalized Envelope and Threshold",AxesLabel->{ToString[lenSeconds]<>" * Samples","Amplitude"}, ImageSize->Scaled[0.8]]

(*6. Bitextraktion*)
bitDuration=samplingRate;
extractedBits=Table[If[Min[Take[normalizedEnvelope,{i,Min[i+bitDuration-1,Length[normalizedEnvelope]]}]]<threshold,1,0],{i,1,Length[normalizedEnvelope]-bitDuration+1,bitDuration}];
Print["Anzahl der extrahierten Bits: ",Length[extractedBits]];

ListPlot[extractedBits[[1;;Min[lenSeconds,Length[extractedBits]]]],PlotRange->All,PlotLabel->"Extracted Bits",AxesLabel->{"Time/Sekunden","Bits"}, ImageSize->Scaled[0.8]]

(*7. Minutenmarker-Erkennung mit Verschiebung*)
minuteMarkerPositions=Table[If[Min[Take[normalizedEnvelope,{i,Min[i+bitDuration-1,Length[normalizedEnvelope]]}]]>thresholdMinutes,1,0],{i,1,Length[normalizedEnvelope]-bitDuration+1,bitDuration}];
minuteMarkerPositions=Drop[Prepend[minuteMarkerPositions,0],-1];

Print["Gefundene Minutenmarker-Positionen (angepasst): ",Position[minuteMarkerPositions,1]];

ListPlot[minuteMarkerPositions[[1;;Min[lenSeconds,Length[minuteMarkerPositions]]]],PlotRange->All,PlotLabel->"Minutenmarker-Erkennung",AxesLabel->{"Time/Sekunden","Marker"}, ImageSize->Scaled[0.8]]

(*Funktion zur Paritätsprüfung*)
CheckParity[bits_,parityBitIndex_,bitRange_]:=Module[{bitSegment,calculatedParity},bitSegment=bits[[bitRange]];
calculatedParity=Mod[Total[bitSegment],2];
Return[calculatedParity==bits[[parityBitIndex]]]]

(*Funktion zur Formatierung und Dekodierung mit Paritätsprüfung*)
FormatDecodeDCF77[bitSegment_]:=Module[{minuteOnesBits,minuteTensBits,hourOnesBits,hourTensBits,dayOnesBits,dayTensBits,monthOnesBits,monthTensBits,yearOnesBits,yearTensBits,minutes,hours,day,month,year,minuteParity,hourParity,dateParity,parityErrors,datumZeit},(*Dekodiere die einzelnen Bitsegmente*)minuteOnesBits=bitSegment[[21;;24]];
minuteTensBits=bitSegment[[25;;27]];
hourOnesBits=bitSegment[[29;;32]];
hourTensBits=bitSegment[[33;;34]];
dayOnesBits=bitSegment[[36;;39]];
dayTensBits=bitSegment[[40;;41]];
monthOnesBits=bitSegment[[45;;48]];
monthTensBits=bitSegment[[49]];
yearOnesBits=bitSegment[[50;;53]];
yearTensBits=bitSegment[[54;;57]];
(*Berechne Minuten,Stunden,Tag,Monat und Jahr*)
minutes=FromDigits[Reverse[minuteOnesBits],2]+10*FromDigits[Reverse[minuteTensBits],2];
hours=FromDigits[Reverse[hourOnesBits],2]+10*FromDigits[Reverse[hourTensBits],2];
day=FromDigits[Reverse[dayOnesBits],2]+10*FromDigits[Reverse[dayTensBits],2];
month=FromDigits[Reverse[monthOnesBits],2]+10*monthTensBits;
year=FromDigits[Reverse[yearOnesBits],2]+10*FromDigits[Reverse[yearTensBits],2]+2000;
(*Paritätsprüfung*)minuteParity=CheckParity[bitSegment,28,21;;27];
hourParity=CheckParity[bitSegment,35,29;;34];
dateParity=CheckParity[bitSegment,58,36;;57];
(*Überprüfung auf Fehler*)parityErrors={};
If[!minuteParity,AppendTo[parityErrors,"Minuten"]];
If[!hourParity,AppendTo[parityErrors,"Stunden"]];
If[!dateParity,AppendTo[parityErrors,"Datum"]];
(*Ausgabe bei korrekten Daten oder bei Fehlern*)datumZeit=IntegerString[day,10,2]<>"."<>IntegerString[month,10,2]<>"."<>ToString[year]<>" - "<>IntegerString[hours,10,2]<>":"<>IntegerString[minutes,10,2];
If[Length[parityErrors]>0,<|"DatumUndZeit"->datumZeit<>" (Paritätsfehler bei: "<>StringJoin[Riffle[parityErrors,", "]]<>")"|>,<|"DatumUndZeit"->datumZeit|>]]

(*Synchronisierungs-und Dekodierungsfunktion mit schöner Ausgabe*)
FormattedSynchronizeAndDecodeDCF77[extractedBits_,minuteMarkerPositions_]:=Module[{decodedData,bitSegments,segmentStart},
(*Synchronisiere Bit-Segmente mit Minutenmarkern*)
cPrint["Minutenmarker-Positionen: ",Position[minuteMarkerPositions,1]];
bitSegments=Table[segmentStart=pos+1;
If[segmentStart+58<=Length[extractedBits],extractedBits[[segmentStart;;segmentStart+58]],{}],{pos,Flatten[Position[minuteMarkerPositions,1]]}];
(*Dekodiere jedes Bit-Segment und formatiere das Ergebnis*)
decodedData=Map[If[Length[#]==59,FormatDecodeDCF77[#],<|"Error"->"Unvollständiges Bit-Segment"|>]&,bitSegments];
(*Ausgabe der dekodierten Daten*)Do[If[KeyExistsQ[decodedData[[i]],"DatumUndZeit"],Print["Segment ",i,": ",decodedData[[i,"DatumUndZeit"]]]],{i,Length[decodedData]}];]

(*Beispielaufruf*)
FormattedSynchronizeAndDecodeDCF77[extractedBits,minuteMarkerPositions];
Beep[]