diff --git a/README b/README
index bc0815b..979e0bd 100644
--- a/README
+++ b/README
@@ -28,7 +28,7 @@ Additionally the following communication services are implemented:
* German classic 'Zeitansage' (talking clock)
* POCSAG transmitter / receiver
* Golay/GSC transmitter / receiver
- * DCF77 time signal transmitter and receiver
+ * DCF77 time signal transmitter and receiver with weather info
* C-Netz SIM emulator
* C-Netz magnetic card emulator
diff --git a/docs/index.html b/docs/index.html
index 9f1be76..806fb82 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -132,6 +132,7 @@ Additional features:
POCSAG (paging system)
Golay / GSC (paging system)
5-Ton-Ruf (firefighter's pagers and siren control)
+ DCF77 The German longwave time signal transmitter/receiver
diff --git a/src/dcf77/Makefile.am b/src/dcf77/Makefile.am
index 22c133d..ce23ac8 100644
--- a/src/dcf77/Makefile.am
+++ b/src/dcf77/Makefile.am
@@ -6,6 +6,9 @@ bin_PROGRAMS = \
dcf77_SOURCES = \
dcf77.c \
+ weather.c \
+ cities.c \
+ image.c \
main.c
dcf77_LDADD = \
$(COMMON_LA) \
diff --git a/src/dcf77/cities.c b/src/dcf77/cities.c
new file mode 100644
index 0000000..61aa8a7
--- /dev/null
+++ b/src/dcf77/cities.c
@@ -0,0 +1,633 @@
+#define _GNU_SOURCE
+#include
+#include
+#include "cities.h"
+
+static struct city_list {
+ int region;
+ const char *name;
+} city_list[] = {
+ { 0, "AGEN" },
+ { 0, "AUCH" },
+ { 0, "BORDEAUX" },
+ { 0, "BRIVE LA GAILLARDE" },
+ { 0, "CAHORS" },
+ { 0, "MONTAUBAN" },
+ { 0, "MONT MARSAN" },
+ { 0, "PAU" },
+ { 0, "PERIGUEUX" },
+ { 0, "TARBES" },
+ { 0, "TOULOUSE" },
+ { 1, "ANGOULEME" },
+ { 1, "LA ROCHELL" },
+ { 1, "LA ROCHE S" },
+ { 1, "LIMOGES" },
+ { 1, "NIORT" },
+ { 1, "POITIERS" },
+ { 2, "ALENCON" },
+ { 2, "AUXERRE" },
+ { 2, "BAR LE DUC" },
+ { 2, "BLOIS" },
+ { 2, "BOBIGNY" },
+ { 2, "BOURGES" },
+ { 2, "CERGY PONT" },
+ { 2, "CHARTRES" },
+ { 2, "CRETEIL" },
+ { 2, "EVRY" },
+ { 2, "LE MANS" },
+ { 2, "MELUN" },
+ { 2, "NANTERRE" },
+ { 2, "NEVERS" },
+ { 2, "ORLEANS" },
+ { 2, "PARIS" },
+ { 2, "REIMS" },
+ { 2, "TOURS" },
+ { 2, "TROYES" },
+ { 2, "VERSAILLES" },
+ { 3, "ANGERS" },
+ { 3, "BREST" },
+ { 3, "CHERBOURG" },
+ { 3, "JERSEY" },
+ { 3, "LAVAL" },
+ { 3, "LORIENT" },
+ { 3, "NANTES" },
+ { 3, "RENNES" },
+ { 3, "ST BRIEUC" },
+ { 4, "AURILLAC" },
+ { 4, "CLERMON FERRAND" },
+ { 4, "FLORAC" },
+ { 4, "GUERET" },
+ { 4, "MENDE" },
+ { 4, "MILLAU" },
+ { 4, "MONTLUCON" },
+ { 4, "PUY VELAY" },
+ { 4, "RODEZ" },
+ { 4, "ST-ETIENNE" },
+ { 4, "ST FLOUR" },
+ { 5, "ALBI" },
+ { 5, "BEZIERS" },
+ { 5, "CARCASSONN" },
+ { 5, "FOIX" },
+ { 5, "MONTPELLIER" },
+ { 5, "PERPIGNAN" },
+ { 6, "AALST" },
+ { 6, "ANTWERPEN" },
+ { 6, "BOULOGNE" },
+ { 6, "BRUGGE" },
+ { 6, "BRUSSEL" },
+ { 6, "CHARLEROI" },
+ { 6, "GENK" },
+ { 6, "GENT" },
+ { 6, "HALLE" },
+ { 6, "HASSELT" },
+ { 6, "IXELLES" },
+ { 6, "KNOKKE-HEIST" },
+ { 6, "KORTRIJK" },
+ { 6, "LEUVEN" },
+ { 6, "LIEGE" },
+ { 6, "LILLE" },
+ { 6, "LOKEREN" },
+ { 6, "MAASTRICHT" },
+ { 6, "MECHELEN" },
+ { 6, "MIDDELBURG" },
+ { 6, "MONS" },
+ { 6, "MOUSCRON" },
+ { 6, "NAMUR" },
+ { 6, "OOSTENDE" },
+ { 6, "ROESELARE" },
+ { 6, "SCHAERBEEK" },
+ { 6, "TERNEUZEN" },
+ { 6, "TOURNAI" },
+ { 7, "CHAUMONT" },
+ { 7, "DIJON" },
+ { 7, "EPINAL" },
+ { 7, "LONS LE S" },
+ { 7, "METZ" },
+ { 7, "NANCY" },
+ { 7, "VESOUL" },
+ { 8, "ALES" },
+ { 8, "AVIGNON" },
+ { 8, "MARSEILLE" },
+ { 8, "MONTELIMAR" },
+ { 8, "NIMES" },
+ { 8, "PRIVAS" },
+ { 8, "ST TROPEZ" },
+ { 8, "TOULON" },
+ { 9, "BOURG EN B" },
+ { 9, "LYON" },
+ { 9, "MACON" },
+ { 9, "VALENCE" },
+ { 10, "BRIANCON" },
+ { 10, "CHAMBERY" },
+ { 10, "DIGNE" },
+ { 10, "GAP" },
+ { 10, "GRENOBLE" },
+ { 11, "ANNECY" },
+ { 11, "BESANCON" },
+ { 11, "DELEMONT" },
+ { 11, "LA CHAUX-DE-FONDS" },
+ { 12, "ASCHAFFENBURG" },
+ { 12, "BAD HOMBURG" },
+ { 12, "BAD KREUZNACH" },
+ { 12, "DARMSTADT" },
+ { 12, "FRANKFURT AM MAIN" },
+ { 12, "HEIDELBERG" },
+ { 12, "KAISERSLAUTERN" },
+ { 12, "KARLSRUHE" },
+ { 12, "LANDAU IN DER PFALZ" },
+ { 12, "LUDWIGSHAFEN" },
+ { 12, "MAINZ" },
+ { 12, "MANNHEIM" },
+ { 12, "PIRMASENS" },
+ { 12, "WORMS" },
+ { 13, "BITBURG" },
+ { 13, "HAGEN" },
+ { 13, "ISERLOHN" },
+ { 13, "KOBLENZ" },
+ { 13, "LUEDENSCHEID" },
+ { 13, "LUXEMBOURG" },
+ { 13, "NEUWIED" },
+ { 13, "SAARBRUECKEN" },
+ { 13, "SEDAN" },
+ { 13, "SIEGEN" },
+ { 13, "TRIER" },
+ { 13, "VERVIERS" },
+ { 13, "WIESBADEN" },
+ { 14, "AACHEN" },
+ { 14, "BIELEFELD" },
+ { 14, "BOCHUM" },
+ { 14, "BONN" },
+ { 14, "DORTMUND" },
+ { 14, "DUEREN" },
+ { 14, "DUESSELDORF" },
+ { 14, "DUISBURG" },
+ { 14, "ESSEN" },
+ { 14, "GELSENKIRCHEN" },
+ { 14, "GUETERSLOH" },
+ { 14, "KOELN" },
+ { 14, "LINGEN" },
+ { 14, "LIPPSTADT" },
+ { 14, "MOENCHENGLADBACH" },
+ { 14, "MUELHEIM AN DER RUHR" },
+ { 14, "MUENSTER" },
+ { 14, "NORDHORN" },
+ { 14, "OBERHAUSEN" },
+ { 14, "OSNABRUECK" },
+ { 14, "RECKLINGHAUSEN" },
+ { 14, "RHEINE" },
+ { 14, "SOLINGEN" },
+ { 14, "WESEL" },
+ { 14, "WUPPERTAL" },
+ { 15, "BRISTOL" },
+ { 15, "CARDIFF" },
+ { 15, "EXETER" },
+ { 15, "HOLYHEAD" },
+ { 15, "PLYMOUTH" },
+ { 15, "ST DAVIDS" },
+ { 15, "SWANSEA" },
+ { 16, "BIRMINGHAM" },
+ { 16, "BLACKPOOL" },
+ { 16, "LEEDS" },
+ { 16, "LEICESTER" },
+ { 16, "LIVERPOOL" },
+ { 16, "MANCHESTER" },
+ { 16, "MIDDLESBROUGH" },
+ { 16, "NEWCASTLE" },
+ { 16, "NOTTINGHAM" },
+ { 16, "SHEFFIELD" },
+ { 17, "AMIENS" },
+ { 17, "BEAUVAIS" },
+ { 17, "CAEN" },
+ { 17, "EVREUX" },
+ { 17, "LAON" },
+ { 17, "LE HAVRE" },
+ { 17, "ROUEN" },
+ { 18, "BOURNEMOUT" },
+ { 18, "BRIGHTON" },
+ { 18, "CAMBRIDGE" },
+ { 18, "DOVER" },
+ { 18, "IPSWICH" },
+ { 18, "KINGSTON" },
+ { 18, "LONDON" },
+ { 18, "NORTHAMPTON" },
+ { 18, "NORWICH" },
+ { 18, "OXFORD" },
+ { 18, "PORTSMOUTH" },
+ { 18, "READING" },
+ { 18, "SOUTHAMPTON" },
+ { 19, "BORKUM" },
+ { 19, "BREMERHAVEN" },
+ { 19, "CUXHAVEN" },
+ { 19, "DEN HELDER" },
+ { 19, "ELMSHORN" },
+ { 19, "EMDEN" },
+ { 19, "GRONINGEN" },
+ { 19, "HAMBURG" },
+ { 19, "LEEUWARDEN" },
+ { 19, "NORDERTSTEDT" },
+ { 19, "SPIEKEROOG" },
+ { 19, "ST PETER ORDING" },
+ { 19, "SYLT" },
+ { 19, "TEXEL" },
+ { 19, "WILHELMSHAVEN" },
+ { 20, "ALBORG" },
+ { 20, "ESBJERG" },
+ { 20, "FREDERIKSHAVN" },
+ { 20, "HERNING" },
+ { 20, "HOLSTERBRO" },
+ { 20, "SKAGEN" },
+ { 20, "THISTED" },
+ { 20, "THYBOROEN" },
+ { 20, "VIBORG" },
+ { 21, "ARHUS" },
+ { 21, "FREDERICIA" },
+ { 21, "HORSENS" },
+ { 21, "KOLDING" },
+ { 21, "ODENSE" },
+ { 21, "RANDERS" },
+ { 21, "SILKEBORG" },
+ { 21, "VEJLE" },
+ { 22, "BRAUNSCHWEIG" },
+ { 22, "BREMEN" },
+ { 22, "CELLE" },
+ { 22, "GOSLAR" },
+ { 22, "HAMELN" },
+ { 22, "HANNOVER" },
+ { 22, "HERFORD" },
+ { 22, "HILDESHEIM" },
+ { 22, "LUENEBURG" },
+ { 22, "MAGDEBURG" },
+ { 22, "MINDEN" },
+ { 22, "OLDENBURG" },
+ { 22, "WOLFSBURG" },
+ { 23, "HELSINGOER" },
+ { 23, "KALUNDBORG" },
+ { 23, "KOEBENHAVN" },
+ { 23, "MALMOE" },
+ { 23, "NAESTVED" },
+ { 23, "ROSKILDE" },
+ { 23, "SLAGELSE" },
+ { 24, "FEHMARN" },
+ { 24, "FLENSBURG" },
+ { 24, "GREIFSWALD" },
+ { 24, "KIEL" },
+ { 24, "LUEBECK" },
+ { 24, "ROSTOCK" },
+ { 24, "RUEGEN" },
+ { 24, "SCHWERIN" },
+ { 24, "STRALSUND" },
+ { 24, "SZCZECIN" },
+ { 24, "WISMAR" },
+ { 25, "AUGSBURG" },
+ { 25, "DEGGENDORF" },
+ { 25, "INGOLSTADT" },
+ { 25, "LANDSHUT" },
+ { 25, "PASSAU" },
+ { 25, "REGENSBURG" },
+ { 25, "ULM" },
+ { 26, "BURGHAUSEN" },
+ { 26, "FRIEDRICHSHAFEN" },
+ { 26, "KEMPTEN" },
+ { 26, "LINZ" },
+ { 26, "MUENCHEN" },
+ { 26, "ROSENHEIM" },
+ { 26, "SIGMARINGEN" },
+ { 26, "WELS" },
+ { 27, "BOLZANO" },
+ { 27, "MERANO" },
+ { 27, "TRENTO" },
+ { 28, "ANSBACH" },
+ { 28, "BAMBERG" },
+ { 28, "BAYREUTH" },
+ { 28, "ERLANGEN" },
+ { 28, "FUERTH" },
+ { 28, "NUERNBERG" },
+ { 28, "SCHWEINFURT" },
+ { 28, "WEIDEN" },
+ { 28, "WERTHEIM" },
+ { 28, "WUERZBURG" },
+ { 29, "ALTENBURG" },
+ { 29, "BAUTZEN" },
+ { 29, "CHEMNITZ" },
+ { 29, "COTTBUS" },
+ { 29, "DESSAU" },
+ { 29, "DRESDEN" },
+ { 29, "EISENHUETTENSTADT" },
+ { 29, "GOERLITZ" },
+ { 29, "HALLE" },
+ { 29, "HOYERSWERDA" },
+ { 29, "LEIPZIG" },
+ { 29, "WITTENBERG" },
+ { 29, "WROCLAW" },
+ { 30, "EISENACH" },
+ { 30, "ERFURT" },
+ { 30, "GOTHA" },
+ { 30, "HOF" },
+ { 30, "JENA" },
+ { 30, "NORDHAUSEN" },
+ { 30, "PLAUEN" },
+ { 30, "SUHL" },
+ { 30, "WEIMAR" },
+ { 30, "ZWICKAU" },
+ { 31, "EVIAN" },
+ { 31, "FRIBOURG" },
+ { 31, "GENEVE" },
+ { 31, "LAUSANNE" },
+ { 31, "MONTREUX" },
+ { 31, "NEUCHATEL" },
+ { 32, "AARAU" },
+ { 32, "BERN" },
+ { 32, "BIENNE" },
+ { 32, "FRAUENFELD" },
+ { 32, "KONSTANZ" },
+ { 32, "LUZERN" },
+ { 32, "SCHAFFHAUSEN" },
+ { 32, "SOLOTHURN" },
+ { 32, "ZUERICH" },
+ { 32, "ZUG" },
+ { 33, "ADELBODEN" },
+ { 33, "GRINDELWALD" },
+ { 33, "INTERLAKEN" },
+ { 34, "BRIG" },
+ { 34, "MARTIGNY" },
+ { 34, "SION" },
+ { 35, "ALTDORF" },
+ { 35, "GLARUS" },
+ { 35, "SARNEN" },
+ { 35, "SCHWYZ" },
+ { 35, "STANS" },
+ { 35, "ST.GALLEN" },
+ { 36, "CHUR" },
+ { 36, "DAVOS" },
+ { 37, "FULDA" },
+ { 37, "GIESSEN" },
+ { 37, "GOETTINGEN" },
+ { 37, "KASSEL" },
+ { 37, "MARBURG" },
+ { 38, "BELLINZONA" },
+ { 38, "EDOLO" },
+ { 38, "LOCARNO" },
+ { 38, "LUGANO" },
+ { 39, "AOSTA" },
+ { 39, "SESTRIERE" },
+ { 40, "ALESSANDRIA" },
+ { 40, "BERGAMO" },
+ { 40, "BRESCIA" },
+ { 40, "MILANO" },
+ { 40, "PARMA" },
+ { 40, "PIACENZA" },
+ { 40, "TORINO" },
+ { 40, "VERONA" },
+ { 41, "FIRENZE" },
+ { 41, "PERUGIA" },
+ { 41, "PISA" },
+ { 41, "ROMA" },
+ { 41, "SIENA" },
+ { 41, "VATICANO" },
+ { 42, "AMSTERDAM" },
+ { 42, "ARNHEM" },
+ { 42, "ASSEN" },
+ { 42, "DEN HAAG" },
+ { 42, "EINDHOVEN" },
+ { 42, "HAARLEM" },
+ { 42, "LELYSTAD" },
+ { 42, "ROTTERDAM" },
+ { 42, "S.HERTOGENBOSCH" },
+ { 42, "UTRECHT" },
+ { 42, "ZWOLLE" },
+ { 43, "CANNES" },
+ { 43, "GENOVA" },
+ { 43, "LA SPEZIA" },
+ { 43, "MONACO" },
+ { 43, "NICE" },
+ { 43, "SAN REMO" },
+ { 44, "BOLOGNA" },
+ { 44, "NOVA GORIC" },
+ { 44, "RIJEKA" },
+ { 44, "RIMINI" },
+ { 44, "TRIESTE" },
+ { 44, "UDINE" },
+ { 44, "VENEZIA" },
+ { 45, "BASEL" },
+ { 45, "BELFORT" },
+ { 45, "COLMAR" },
+ { 45, "FREIBURG" },
+ { 45, "LIESTAL" },
+ { 45, "LOERRACH" },
+ { 45, "MULHOUSE" },
+ { 45, "OFFENBURG" },
+ { 45, "STRASBOURG" },
+ { 46, "GRAZ" },
+ { 46, "KLAGENFURT" },
+ { 46, "LIENZ" },
+ { 46, "LJUBLJANA" },
+ { 46, "MARIBOR" },
+ { 46, "SEMMERING" },
+ { 46, "VILLACH" },
+ { 46, "ZELTWEG" },
+ { 47, "BAD GASTEIN" },
+ { 47, "INNSBRUCK" },
+ { 47, "ISCHGL" },
+ { 47, "LANDECK" },
+ { 47, "SOELDEN" },
+ { 47, "TUXERTAL" },
+ { 48, "BAD TOELZ" },
+ { 48, "BERCHTESGADEN" },
+ { 48, "BISCHOFSHOFEN" },
+ { 48, "BREGENZ" },
+ { 48, "GARMISCH PARTENKIRCHEN" },
+ { 48, "KITZBUEHEL" },
+ { 48, "LINDAU" },
+ { 48, "SALZBURG" },
+ { 48, "SCHLADMING" },
+ { 48, "STEYR" },
+ { 48, "VADUZ" },
+ { 49, "BRATISLAVA" },
+ { 49, "EISENSTADT" },
+ { 49, "GYOER" },
+ { 49, "KLOSTERNEUBURG" },
+ { 49, "SOPRON" },
+ { 49, "SZOMBATHELY" },
+ { 49, "TRENCIN" },
+ { 49, "WIEN" },
+ { 49, "WIENER NEUSTADT" },
+ { 49, "ZALAEGERSZEG" },
+ { 50, "BRNO" },
+ { 50, "BUDEJOVICE" },
+ { 50, "CHEB" },
+ { 50, "HAVLICKAV BROD" },
+ { 50, "HRADEC/KRA" },
+ { 50, "JIHLAVA" },
+ { 50, "KARLOVY VARY" },
+ { 50, "KLADNO" },
+ { 50, "MOST" },
+ { 50, "OLOMOUC" },
+ { 50, "OSTRAVA" },
+ { 50, "PARDUBICE" },
+ { 50, "PLZEN" },
+ { 50, "PRAHA" },
+ { 50, "PREROV" },
+ { 50, "PROSTEJOV" },
+ { 50, "ST POELTEN" },
+ { 50, "ZWETTL" },
+ { 51, "CHOMUTOV" },
+ { 51, "DECIN" },
+ { 51, "LIBEREC" },
+ { 51, "OPAVA" },
+ { 51, "PIRNA" },
+ { 51, "SNIEZKA" },
+ { 51, "TEPLICE" },
+ { 51, "USTI NAD LABEM" },
+ { 51, "WALBRZYCH" },
+ { 52, "BERLIN" },
+ { 52, "BRANDENBURG" },
+ { 52, "FINOW" },
+ { 52, "FRANKFURT AN DER ODER" },
+ { 52, "GORZOW" },
+ { 52, "NEUBRANDENBURG" },
+ { 52, "POTSDAM" },
+ { 52, "POZNAN" },
+ { 52, "STENDAL" },
+ { 53, "GOETEBORG" },
+ { 53, "HALMSTAD" },
+ { 54, "GAEVLE" },
+ { 54, "NYKOPING" },
+ { 54, "STOCKHOLM" },
+ { 54, "UPPSALA" },
+ { 54, "VAESTERAS" },
+ { 55, "BORGHOLM" },
+ { 55, "BORNHOLM" },
+ { 55, "KALMAR" },
+ { 55, "LINKOEPING" },
+ { 55, "RONNE" },
+ { 55, "VISBY" },
+ { 56, "BORAS" },
+ { 56, "JOENKOEPING" },
+ { 56, "KARLSTAD" },
+ { 56, "OEREBRO" },
+ { 57, "BADEN-BADEN" },
+ { 57, "DONAUESCHINGEN" },
+ { 57, "FREUDENSTADT" },
+ { 57, "VILLINGEN-SCHWENNINGEN" },
+ { 58, "DRAMMEN" },
+ { 58, "FREDRIKSTADEN" },
+ { 58, "OSLO" },
+ { 58, "TOENSBERG" },
+ { 59, "AALEN" },
+ { 59, "GOEPPINGEN" },
+ { 59, "HEILBRONN" },
+ { 59, "PFORZHEIM" },
+ { 59, "REUTLINGEN" },
+ { 59, "SCHWAEBISCH GMUEND" },
+ { 59, "STUTTGART" },
+ { 59, "TUEBINGEN" },
+ { 60, "NAPOLI" },
+ { 61, "ANCONA" },
+ { 61, "PESCARA" },
+ { 61, "SAN MARINO" },
+ { 62, "BARI" },
+ { 62, "FOGGIA" },
+ { 62, "LECCE" },
+ { 63, "BEKESCSABA" },
+ { 63, "BRANSKA" },
+ { 63, "BUDAPEST" },
+ { 63, "DEBRECEN" },
+ { 63, "DUNAIJVAROS" },
+ { 63, "EGER" },
+ { 63, "ERD" },
+ { 63, "HODMEZOVASARHELY" },
+ { 63, "KAPOSVAR" },
+ { 63, "KECSKEMET" },
+ { 63, "KOSICE" },
+ { 63, "MISKOLC" },
+ { 63, "NAGYKANIZSA" },
+ { 63, "NYIREGYHAZA" },
+ { 63, "OZD" },
+ { 63, "PECS" },
+ { 63, "SALGOTARJAN" },
+ { 63, "SIOFOK" },
+ { 63, "SZEGED" },
+ { 63, "SZEKESFEEVAR" },
+ { 63, "SZOLNOK" },
+ { 63, "TATABANYA" },
+ { 63, "VESZPREM" },
+ { 64, "MADRID" },
+ { 65, "BILBAO" },
+ { 66, "CATANIA" },
+ { 66, "COSENZA" },
+ { 66, "MESSINA" },
+ { 66, "PALERMO" },
+ { 66, "REGGIO CALABRIA" },
+ { 67, "IBIZA" },
+ { 67, "MAHON" },
+ { 67, "PALMA DE MALLORCA" },
+ { 68, "VALENCIA" },
+ { 69, "BARCELONA" },
+ { 69, "FIGUERES" },
+ { 69, "GIRONA" },
+ { 69, "LLORET DE MAR" },
+ { 70, "ANDORRA LA VELLA" },
+ { 71, "SEVILLA" },
+ { 72, "LISBOA" },
+ { 73, "AJACCIO" },
+ { 73, "BASTIA" },
+ { 73, "CAGLIARI" },
+ { 73, "SASSARI" },
+ { 74, "GIJON" },
+ { 75, "CORK" },
+ { 75, "GALWAY" },
+ { 75, "LIMERICK" },
+ { 76, "BELFAST" },
+ { 76, "DUBLIN" },
+ { 77, "ABERDEEN" },
+ { 77, "EDINBURGH" },
+ { 77, "GLASGOW" },
+ { 77, "ISLE OF MAN" },
+ { 78, "BERGEN" },
+ { 78, "STAVANGER" },
+ { 79, "TRONDHEIM" },
+ { 80, "SUNDSVALL" },
+ { 81, "GDANSK" },
+ { 81, "OLSZTYN" },
+ { 81, "SLUPSK" },
+ { 82, "BIALYSTOK" },
+ { 82, "BYDGOSZCZ" },
+ { 82, "LODZ" },
+ { 82, "LUBLIN" },
+ { 82, "TORUN" },
+ { 82, "WARSZAWA" },
+ { 83, "BIELSKO" },
+ { 83, "KATOWICE" },
+ { 83, "KIELCE" },
+ { 83, "KRAKOW" },
+ { 83, "OPOLE" },
+ { 83, "RZESZOW" },
+ { 83, "ZAKOPANE" },
+ { 84, "UMEA" },
+ { 85, "FALUN" },
+ { 85, "OESTERSUND" },
+ { 86, "SAMEDAN" },
+ { 87, "OSIJEK" },
+ { 87, "ZAGREB" },
+ { 88, "ZERMATT" },
+ { 89, "SPLIT" },
+ { 24, "Doerphof" },
+ { -1, NULL }
+};
+
+void display_city(const char *search)
+{
+ int i;
+ int found = 0;
+
+ for (i = 0; city_list[i].name; i++) {
+ if (strcasestr(city_list[i].name, search)) {
+ found++;
+ printf("City %s is located in region %d.\n", city_list[i].name, city_list[i].region);
+ }
+ }
+ if (!found) {
+ printf("No city found for '%s', try larger city.\n", search);
+ }
+}
+
diff --git a/src/dcf77/cities.h b/src/dcf77/cities.h
new file mode 100644
index 0000000..2e512fe
--- /dev/null
+++ b/src/dcf77/cities.h
@@ -0,0 +1,3 @@
+
+void display_city(const char *search);
+
diff --git a/src/dcf77/dcf77.c b/src/dcf77/dcf77.c
index 81b41e4..5314803 100644
--- a/src/dcf77/dcf77.c
+++ b/src/dcf77/dcf77.c
@@ -1,5 +1,5 @@
-/* implementation of DCF77 transmitter and receiver
+/* implementation of DCF77 transmitter and receiver, including weather
*
* (C) 2022 by Andreas Eversberg
* All Rights Reserved
@@ -28,6 +28,7 @@
#include
#include "../libdebug/debug.h"
#include "dcf77.h"
+#include "weather.h"
double get_time(void);
@@ -35,7 +36,6 @@ double get_time(void);
#define TEST_FREQUENCY 1000
#define CARRIER_BANDWIDTH 10.0
#define SAMPLE_CLOCK 1000
-#define CLOCK_1S 1.0
#define CLOCK_BANDWIDTH 0.1
#define REDUCTION_FACTOR 0.15
#define REDUCTION_TH 0.575
@@ -43,6 +43,9 @@ double get_time(void);
#define level2db(level) (20 * log10(level))
+/* uncomment to speed up transmission by factor 10 and feed the data to the receiver */
+//#define DEBUG_LOOP
+
static int fast_math = 0;
static float *sin_tab = NULL, *cos_tab = NULL;
@@ -50,6 +53,292 @@ const char *time_zone[4] = { "???", "CEST", "CET", "???" };
const char *week_day[8] = { "???", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
const char *month_name[13] = { "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+const char *datasets_0_59[8] = {
+ "Maximum values 1st day (today)",
+ "Minimum values 1st day (today)",
+ "Maximum values 2nd day (tomorrow)",
+ "Minimum values 2nd day (tomorrow)",
+ "Maximum values 3rd day (in two days)",
+ "Minimum values 3rd day (in two days)",
+ "Maximum values 4th day (in three days)",
+ "Wind and weather anomaly 4th day (in three days)",
+};
+
+const char *datasets_60_89[2] = {
+ "Maximum values 1st day (following day)",
+ "Maximum values 2nd day (2nd following day)",
+};
+
+const char *region[90] = {
+ "F - Bordeaux, Aquitaine (Suedwestfrankreich)",
+ "F - La Rochelle, Poitou-Charentes (Westkueste Frankreichs)",
+ "F - Paris, Ile-de-France (Pariser Becken)",
+ "F - Brest, Bretagne",
+ "F - Clermont-Ferrand (Massif Central), Auvergne (Zentralmassiv)",
+ "F - Beziers, Languedoc-Roussillon",
+ "B - Bruxelles, Brussel (Benelux)",
+ "F - Dijon (Bourgogne), Bourgogne (Ostfrankreich / Burgund)",
+ "F - Marseille, Provence-Alpes-Côte d'Azur",
+ "F - Lyon (Rhone-Alpes), Rhone-Alpes (Rhonetal)",
+ "F - Grenoble (Savoie), Rhone-Alpes (Franz. Alpen)",
+ "CH - La Chaux de Fond, Jura",
+ "D - Frankfurt am Main, Hessen (Unterer Rheingraben)",
+ "D - Trier, Westliches Mittelgebirge",
+ "D - Duisburg, Nordrhein-Westfalen",
+ "GB - Swansea, Wales (Westl. England / Wales)",
+ "GB - Manchester, England (Noerdliches England)",
+ "F - le Havre, Haute-Normandie (Normandie)",
+ "GB - London, England (Suedostengland / London)",
+ "D - Bremerhaven, Bremen (Nordseekueste)",
+ "DK - Herning, Ringkobing (Nordwestliches Juetland)",
+ "DK - Arhus, Arhus (Oestliches Juetland)",
+ "D - Hannover, Niedersachsen (Norddeutschland)",
+ "DK - Kobenhavn, Staden Kobenhaven (Seeland)",
+ "D - Rostock, Mecklenburg-Vorpommern (Ostseekueste)",
+ "D - Ingolstadt, Bayern (Donautal)",
+ "D - Muenchen, Bayern (Suedbayern)",
+ "I - Bolzano, Trentino-Alto Adige (Suedtirol)",
+ "D - Nuernberg, Bayern (Nordbayern)",
+ "D - Leipzig, Sachsen",
+ "D - Erfurt, Thueringen",
+ "CH - Lausanne, Genferseeregion (Westl. Schweizer Mitteland)",
+ "CH - Zuerich (Oestl. Schweizer Mittelland)",
+ "CH - Adelboden (Westl. Schweizer Alpennordhang)",
+ "CH - Sion, Wallis",
+ "CH - Glarus, Oestlicher Schweizer Alpennordhang",
+ "CH - Davos, Graubuenden",
+ "D - Kassel, Hessen (Mittelgebirge Ost)",
+ "CH - Locarno, Tessin",
+ "I - Sestriere, Piemont. Alpen",
+ "I - Milano, Lombardia (Poebene)",
+ "I - Roma, Lazio (Toskana)",
+ "NL - Amsterdam, Noord-Holland (Holland)",
+ "I - Genova, Liguria (Golf von Genua)",
+ "I - Venezia, Veneto (Pomuendung)",
+ "F - Strasbourg, Alsace (Oberer Rheingraben)",
+ "A - Klagenfurt, Kaernten (Oesterreich. Alpensuedhang)",
+ "A - Innsbruck, Tirol (Inneralpine Gebiete Oesterreichs)",
+ "A - Salzburg, Bayr. / Oesterreich. Alpennordhang",
+ "SK (Oesterreich / Slovakia) - Wien / Bratislava",
+ "CZ - Praha, Prag (Tschechisches Becken)",
+ "CZ - Decin, Severocesky (Erzgebirge)",
+ "D - Berlin, Ostdeutschland",
+ "S - Goeteborg, Goeteborgs och Bohus Laen (Westkueste Schweden)",
+ "S - Stockholm, Stockholms Laen (Stockholm)",
+ "S - Kalmar, Kalmar Laen (Schwedische Ostseekueste)",
+ "S - Joenkoeping, Joenkoepings Laen (Suedschweden)",
+ "D - Donaueschingen, Baden-Wuerttemberg (Schwarzwald / Schwaebische Alb)",
+ "N - Oslo",
+ "D - Stuttgart, Baden-Wuerttemberg (Noerdl. Baden Wuerttemberg)",
+ "I - Napoli",
+ "I - Ancona",
+ "I - Bari",
+ "HU - Budapest",
+ "E - Madrid",
+ "E - Bilbao",
+ "I - Palermo",
+ "E - Palma de Mallorca",
+ "E - Valencia",
+ "E - Barcelona",
+ "AND - Andorra",
+ "E - Sevilla",
+ "P - Lissabon",
+ "I - Sassari, (Sardinien / Korsika)",
+ "E - Gijon",
+ "IRL - Galway",
+ "IRL - Dublin",
+ "GB - Glasgow",
+ "N - Stavanger",
+ "N - Trondheim",
+ "S - Sundsvall",
+ "PL - Gdansk",
+ "PL - Warszawa",
+ "PL - Krakow",
+ "S - Umea",
+ "S - Oestersund",
+ "CH - Samedan",
+ "CR - Zagreb",
+ "CH - Zermatt",
+ "CR - Split",
+};
+
+const char *weathers_day[16] = {
+ "Reserved",
+ "Sunny",
+ "Partly clouded",
+ "Mostly clouded",
+ "Overcast",
+ "Heat storms",
+ "Heavy Rain",
+ "Snow",
+ "Fog",
+ "Sleet",
+ "Rain shower",
+ "Light rain",
+ "Snow showers",
+ "Frontal storms",
+ "Stratus cloud",
+ "Sleet storms",
+};
+
+const char *weathers_night[16] = {
+ "Reserved",
+ "Clear",
+ "Partly clouded",
+ "Mostly clouded",
+ "Overcast",
+ "Heat storms",
+ "Heavy Rain",
+ "Snow",
+ "Fog",
+ "Sleet",
+ "Rain shower",
+ "Light rain",
+ "Snow showers",
+ "Frontal storms",
+ "Stratus cloud",
+ "Sleet storms",
+};
+
+const char *extremeweathers[16] = {
+ "None",
+ "Heavy Weather 24 hrs.",
+ "Heavy weather Day",
+ "Heavy weather Night",
+ "Storm 24hrs.",
+ "Storm Day",
+ "Storm Night",
+ "Wind gusts Day",
+ "Wind gusts Night",
+ "Icy rain morning",
+ "Icy rain evening",
+ "Icy rain night",
+ "Fine dust",
+ "Ozon",
+ "Radiation",
+ "High water",
+};
+
+const char *probabilities[8] = {
+ "0 %",
+ "15 %",
+ "30 %",
+ "45 %",
+ "60 %",
+ "75 %",
+ "90 %",
+ "100 %",
+};
+
+const char *winddirections[16] = {
+ "North",
+ "Northeast",
+ "East",
+ "Southeast",
+ "South",
+ "Southwest",
+ "West",
+ "Northwest",
+ "Changeable",
+ "Foen",
+ "Biese N/O",
+ "Mistral N",
+ "Scirocco S",
+ "Tramont W",
+ "reserved",
+ "reserved",
+};
+
+const char *windstrengths[8] = {
+ "0",
+ "0-2",
+ "3-4",
+ "5-6",
+ "7",
+ "8",
+ "9",
+ ">=10",
+};
+
+const char *yesno[2] = {
+ "No",
+ "Yes",
+};
+const char *anomaly1[4] = {
+ "Same Weather",
+ "Jump 1",
+ "Jump 2",
+ "Jump 3",
+};
+const char *anomaly2[4] = {
+ "0-2 hrs",
+ "2-4 hrs",
+ "5-6 hrs",
+ "7-8 hrs",
+};
+
+/* show a list of weather data values */
+void list_weather(void)
+{
+ time_t timestamp, t;
+ struct tm *tm;
+ int i, j;
+
+ /* get time stamp of this day, but at 22:00 UTC */
+ timestamp = floor(get_time());
+ timestamp -= timestamp % 86400;
+ timestamp += 79200;
+
+ printf("\n");
+ printf("List of all regions\n");
+ printf("-------------------\n");
+ for (i = 0; i < 90; i++) {
+ printf("Region: %2d = %s\n", i, region[i]);
+ for (j = 0; j < 8; j++) {
+ /* get local time where transmission starts */
+ if (i < 60) {
+ t = timestamp + 180 * i + 10800 * j;
+ tm = localtime(&t);
+ printf(" -> Transmission at %02d:%02d of %s\n", tm->tm_hour, tm->tm_min, datasets_0_59[j]);
+ } else if (j < 2) {
+ t = timestamp + 180 * (i - 60) + 10800 * 7 + 5400 * j;
+ tm = localtime(&t);
+ printf(" -> Transmission at %02d:%02d of %s\n", tm->tm_hour, tm->tm_min, datasets_60_89[j]);
+ }
+ }
+ }
+
+ printf("\n");
+ printf("List of all weathers\n");
+ printf("--------------------\n");
+ for (i = 0; i < 16; i++) {
+ if (i == 1)
+ printf("Weather: %2d = %s (day) %s (night)\n", i, weathers_day[i], weathers_night[i]);
+ else
+ printf("Weather: %2d = %s (day and night)\n", i, weathers_day[i]);
+ }
+ printf("\n");
+
+ printf("List of all extreme weathers\n");
+ printf("----------------------------\n");
+ for (i = 1; i < 16; i++) {
+ printf("Extreme: %2d = %s\n", i, extremeweathers[i]);
+ }
+ printf("\n");
+}
+
+static const char *show_bits(uint64_t value, int bits)
+{
+ static char bit[128];
+ int i;
+
+ for (i = 0; i < bits; i++)
+ bit[i] = '0' + ((value >> i) & 1);
+ sprintf(bit + i, "(%" PRIu64 ")", value);
+
+ return bit;
+}
+
/* global init */
int dcf77_init(int _fast_math)
{
@@ -82,6 +371,7 @@ void dcf77_exit(void)
}
}
+/* instance creation */
dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
{
dcf77_t *dcf77 = NULL;
@@ -150,6 +440,7 @@ dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
dcf77->dmp_input_level = display_measurements_add(&dcf77->dispmeas, "Input Level", "%.0f dB", DISPLAY_MEAS_AVG, DISPLAY_MEAS_LEFT, -100.0, 0.0, -INFINITY);
dcf77->dmp_signal_level = display_measurements_add(&dcf77->dispmeas, "Signal Level", "%.0f dB", DISPLAY_MEAS_AVG, DISPLAY_MEAS_LEFT, -100.0, 0.0, -INFINITY);
dcf77->dmp_signal_quality = display_measurements_add(&dcf77->dispmeas, "Signal Qualtiy", "%.0f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, -INFINITY);
+ dcf77->dmp_current_second = display_measurements_add(&dcf77->dispmeas, "Current Second", "%.0f", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 59.0, -INFINITY);
}
if (tx->enable)
@@ -157,9 +448,17 @@ dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
if (rx->enable)
PDEBUG(DDCF77, DEBUG_INFO, "DCF77 receiver has been created.\n");
+#if 0
+ void rx_frame_test(dcf77_t *dcf77, const char *string);
+ rx_frame_test(dcf77, "00001000011111100100101101001010000110000110011100001010000");
+ rx_frame_test(dcf77, "00101010111111000100111101000010000110000110011100001010000");
+ rx_frame_test(dcf77, "00011000101111000100100011000010000110000110011100001010000");
+#endif
+
return dcf77;
}
+/* instance destruction */
void dcf77_destroy(dcf77_t *dcf77)
{
if (dcf77) {
@@ -171,33 +470,264 @@ void dcf77_destroy(dcf77_t *dcf77)
PDEBUG(DDCF77, DEBUG_INFO, "DCF77 has been destroyed.\n");
}
-/* set inital time stamp at the moment the stream starts */
-void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp)
+static void display_weather_temperature(const char *desc, uint32_t weather)
+{
+ int value;
+
+ value = (weather >> 16) & 0x3f;
+ switch (value) {
+ case 0:
+ printf("%s%s = < -21 degrees C\n", desc, show_bits(value, 6));
+ break;
+ case 63:
+ printf("%s%s = > 40 degrees C\n", desc, show_bits(value, 6));
+ break;
+ default:
+ printf("%s%s = %d degrees C\n", desc, show_bits(value, 6), value - 22);
+ }
+}
+
+/*
+ * TX part
+ */
+
+/* Adjust given time stamp to the time stamp when the weather of the given
+ * region istransmitted. An offset is used to start several minutes before
+ * the transmission of the region starts, so the receiver has time to sync
+ * to the signal first, so it will not miss that weather info.
+ *
+ * Note that this will only set the start of transmitting weather of the
+ * current day (and day temperature).
+ */
+time_t dcf77_start_weather(time_t timestamp, int region, int offset)
+{
+ int hour, min;
+
+ /* hour+min at UTC */
+ if (region < 60) {
+ /* first dataset starts at 22:00 UTC */
+ hour = (22 + region / 20) % 24;
+ } else {
+ /* seventh dataset starts at 19:00 UTC */
+ hour = (19 + (region - 60) / 20) % 24;
+ }
+ min = (region % 20) * 3;
+ PDEBUG(DDCF77, DEBUG_INFO, "Setting UTC time for region %d to %02d:%02d minutes.\n", region, hour, min);
+
+ /* reset to 0:00 UTC at same day */
+ timestamp -= (timestamp % 86400);
+
+ /* add time to start */
+ timestamp += hour * 3600 + min * 60;
+
+ /* substract offset */
+ PDEBUG(DDCF77, DEBUG_INFO, "Setting timestamp offset to %d minutes.\n", offset);
+ timestamp -= 60 * offset;
+
+ return timestamp;
+}
+
+/* set weather to transmit on all regions */
+void dcf77_set_weather(dcf77_t *dcf77, int weather_day, int weather_night, int extreme, int rain, int wind_dir, int wind_bft, int temperature_day, int temperature_night)
{
dcf77_tx_t *tx = &dcf77->tx;
- double now;
- time_t t;
- /* get time stamp */
- if (timestamp < 0)
- now = get_time();
+ tx->weather = 1;
+ tx->weather_day = weather_day;
+ tx->weather_night = weather_night;
+ tx->rain = rain;
+ tx->extreme = extreme;
+ tx->wind_dir = wind_dir;
+ tx->wind_bft = wind_bft;
+ tx->temperature_day = temperature_day;
+ tx->temperature_night = temperature_night;
+}
+
+/* generate weather frame from weather data */
+static uint64_t generate_weather(time_t timestamp, int minute, int utc_hour, int weather_day, int weather_night, int extreme, int rain, int wind_dir, int wind_bft, int temperature_day, int temperature_night)
+{
+ int dataset = ((utc_hour + 2) % 24) * 20 + (minute / 3); /* data sets since 22:00 UTC */
+ struct tm *tm;
+ uint64_t key;
+ uint32_t weather;
+ int value, temperature;
+ int i;
+ int best, diff;
+
+ /* generate key from time stamp of next minute */
+ timestamp += 60;
+ tm = localtime(×tamp);
+ key = 0;
+ key |= (uint64_t)(tm->tm_min % 10) << 0;
+ key |= (uint64_t)(tm->tm_min / 10) << 4;
+ key |= (uint64_t)(tm->tm_hour % 10) << 8;
+ key |= (uint64_t)(tm->tm_hour / 10) << 12;
+ key |= (uint64_t)(tm->tm_mday % 10) << 16;
+ key |= (uint64_t)(tm->tm_mday / 10) << 20;
+ key |= (uint64_t)((tm->tm_mon + 1) % 10) << 24;
+ key |= (uint64_t)((tm->tm_mon + 1) / 10) << 28;
+ if (tm->tm_wday > 0)
+ key |= (uint64_t)(tm->tm_wday) << 29;
else
- now = timestamp;
- t = floor(now);
+ key |= (uint64_t)(7) << 29;
+ key |= (uint64_t)(tm->tm_year % 10) << 32;
+ key |= (uint64_t)((tm->tm_year / 10) % 10) << 36;
+
+ /* generate weather data */
+ timestamp -= 120;
+ weather = 0;
+ PDEBUG(DFRAME, DEBUG_INFO, "Encoding weather for dataset %d/480\n", dataset);
+ printf("Peparing Weather INFO\n");
+ printf("---------------------\n");
+ printf("Time (UTC): %02d:%02d\n", (int)(timestamp / 3600) % 24, (int)(timestamp / 60) % 60);
+ /* dataset and region for 0..59 */
+ printf("Dataset: %s\n", datasets_0_59[dataset / 60]);
+ value = dataset % 60;
+ printf("Region: %d = %s\n", value, region[value]);
+ /* calc. weather of region */
+ if (weather_day < 0 || weather_day > 15)
+ weather_day = 1;
+ weather |= weather_day << 0;
+ if (weather_night < 0 || weather_night > 15)
+ weather_night = 1;
+ weather |= weather_night << 4;
+ /* calc. temperature of region 0..59 (day/night) or region 60..89 (day) */
+ if (((dataset / 60) & 1) == 0 || (dataset / 60) == 7)
+ temperature = temperature_day + 22;
+ else
+ temperature = temperature_night + 22;
+ if (temperature < 0)
+ temperature = 0;
+ if (temperature > 63)
+ value = 63;
+ weather |= temperature << 16;
+ /* show weather of region 0..59 */
+ if ((dataset / 60) < 7) {
+ printf("Weather (day): %s = %s\n", show_bits(weather_day, 4), weathers_day[weather_day]);
+ printf("Weather (night): %s = %s\n", show_bits(weather_night, 4), weathers_night[weather_night]);
+ }
+ /* show extreme/wind/rain of region 0..59 */
+ if (((dataset / 60) & 1) == 0) {
+ /* even datasets, this is 'Day' data */
+ if (extreme < 0 || extreme > 15)
+ value = 0;
+ else
+ value = extreme;
+ printf("Extreme weather: %s = %s\n", show_bits(value, 4), extremeweathers[value]);
+ weather |= value << 8;
+ best = 0;
+ for (i = 0; i < 8; i++) {
+ diff = abs(atoi(probabilities[i]) - rain);
+ if (i == 0 || diff < best) {
+ best = diff;
+ value = i;
+ }
+ }
+ printf("Rain Probability: %s = %s (best match for %d)\n", show_bits(value, 3), probabilities[value], rain);
+ weather |= value << 12;
+ value = 0;
+ printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
+ weather |= value << 15;
+ display_weather_temperature("Temperature (day): ", weather);
+ } else {
+ /* odd datasets, this is 'Night' data */
+ if (wind_dir < 0 || wind_dir > 15)
+ value = 8;
+ else
+ value = wind_dir;
+ printf("Wind direction: %s = %s\n", show_bits(value, 4), winddirections[value]);
+ weather |= value << 8;
+ if (wind_bft < 1)
+ value = 0;
+ else
+ if (wind_bft < 7)
+ value = (wind_bft + 1) / 2;
+ else
+ if (wind_bft < 10)
+ value = wind_bft - 3;
+ else
+ value = 7;
+ printf("Wind strength: %s = %s (best match for %d)\n", show_bits(value, 3), windstrengths[value], wind_bft);
+ weather |= value << 12;
+ value = 0;
+ printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
+ weather |= value << 15;
+ value = temperature_night + 22;
+ if (value < 0)
+ value = 0;
+ if (value > 63)
+ value = 63;
+ weather |= value << 16;
+ if ((dataset / 60) < 7)
+ display_weather_temperature("Temperature (night): ", weather);
+ }
+ /* show weather and temperature of of region 60..89 */
+ if ((dataset / 60) == 7) {
+ printf("Dataset: %s\n", 60 + datasets_60_89[(dataset % 60) / 30]);
+ value = 60 + (dataset % 30);
+ printf("Region: %d = %s\n", value, region[value]);
+ printf("Weather (day): %s = %s\n", show_bits(weather_day, 4), weathers_day[weather_day]);
+ printf("Weather (night): %s = %s\n", show_bits(weather_night, 4), weathers_night[weather_night]);
+ display_weather_temperature("Temperature: ", weather);
+ }
+
+ /* the magic '10' bit string */
+ weather |= 0x1 << 22;
+
+ /* encode */
+ return weather_encode(weather, key);
+}
+
+/* transmit chunk of weather data for each minute */
+static uint16_t tx_weather(dcf77_tx_t *tx, time_t timestamp, int minute, int hour, int zone)
+{
+ int index = (minute + 2) % 3;
+ int utc_hour;
+ uint16_t chunk;
+
+ if (index == 0) {
+ /* convert hour to UTC */
+ utc_hour = hour - 1;
+ if (zone & 1)
+ utc_hour--;
+ if (utc_hour < 0)
+ utc_hour += 24;
+ /* in index 0 we transmit minute + 1 (next minute), so we substract 1 */
+ tx->weather_cipher = generate_weather(timestamp, (minute + 59) % 60, utc_hour, tx->weather_day, tx->weather_night, tx->extreme, tx->rain, tx->wind_dir, tx->wind_bft, tx->temperature_day, tx->temperature_night);
+ PDEBUG(DFRAME, DEBUG_INFO, "Transmitting first chunk of weather info.\n");
+ chunk = (tx->weather_cipher & 0x3f) << 1; /* bit 2-7 */
+ chunk |= (tx->weather_cipher & 0x0fc0) << 2; /* bit 9-14 */
+ tx->weather_cipher >>= 12;
+ return chunk;
+ }
+
+ PDEBUG(DFRAME, DEBUG_INFO, "Transmitting %s chunk of weather info.\n", (index == 1) ? "second" : "third");
+ chunk = tx->weather_cipher & 0x3fff;
+ tx->weather_cipher >>= 14;
+ return chunk;
+}
+
+/* set inital time stamp at the moment the stream starts */
+void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp, double sub_sec)
+{
+ dcf77_tx_t *tx = &dcf77->tx;
+
/* current second within minute */
- tx->second = t % 60;
+ tx->second = timestamp % 60;
/* time stamp of next minute */
- tx->timestamp = t - tx->second + 60;
+ tx->timestamp = timestamp - tx->second + 60;
/* wave within current second */
- tx->wave = floor(fmod(now, 1.0) * (double)tx->waves_sec);
+ tx->wave = sub_sec * (double)tx->waves_sec;
/* silence until next second begins */
tx->symbol = 'm'; tx->level = 0;
}
+/* transmit one symbol = one second */
static char tx_symbol(dcf77_t *dcf77, time_t timestamp, int second)
{
dcf77_tx_t *tx = &dcf77->tx;
char symbol;
+ int i, j;
/* generate frame */
if (second == 0 || !tx->data_frame) {
@@ -205,18 +735,19 @@ static char tx_symbol(dcf77_t *dcf77, time_t timestamp, int second)
int isdst_next_hour, wday, zone;
uint64_t frame = 0, p;
+ /* get DST next hour */
timestamp += 3600;
tm = localtime(×tamp);
timestamp -= 3600;
- if (!tm) {
-error_tm:
- PDEBUG(DDCF77, DEBUG_ERROR, "Failed to get local time of time stamp!\n");
- return 'm';
- }
isdst_next_hour = tm->tm_isdst;
tm = localtime(×tamp);
- if (!tm)
- goto error_tm;
+
+ /* get weather data */
+ if (tx->weather) {
+ frame |= tx_weather(tx, timestamp, tm->tm_min, tm->tm_hour, (tm->tm_isdst > 0) ? 1 : 2) << 1;
+ /* calling tx_weather() destroys tm, because it is a pointer to a global variable. now we fix it */
+ tm = localtime(×tamp);
+ }
if (tm->tm_wday > 0)
wday = tm->tm_wday;
@@ -228,7 +759,7 @@ error_tm:
else
zone = 2;
- PDEBUG(DDCF77, DEBUG_NOTICE, "The time transmitting: %s %s %d %02d:%02d:00 %s %02d\n", week_day[wday], month_name[tm->tm_mon + 1], tm->tm_mday, tm->tm_hour, tm->tm_min, time_zone[zone], tm->tm_year + 1900);
+ PDEBUG(DDCF77, DEBUG_NOTICE, "The time transmitting: %s %s %d %02d:%02d:%02d %s %02d\n", week_day[wday], month_name[tm->tm_mon + 1], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, time_zone[zone], tm->tm_year + 1900);
if ((tm->tm_isdst > 0) != (isdst_next_hour > 0))
frame |= (uint64_t)1 << 16;
@@ -270,19 +801,40 @@ error_tm:
frame |= (uint64_t)(p & 1) << 58;
tx->data_frame = frame;
+
+ for (i = 0, j = 0; i < 59; i++) {
+ if (i == 1 || i == 15 || i == 21 || i == 29 || i == 36 || i == 42 || i == 45 || i == 50)
+ tx->data_string[j++] = ' ';
+ tx->data_string[j++] = '0' + ((frame >> i) & 1);
+ }
+ tx->data_string[j] = '\0';
+ PDEBUG(DDSP, DEBUG_INFO, "Start transmission of frame:\n");
+ PDEBUG(DDSP, DEBUG_INFO, "0 Wetterdaten Info 1 Minute P StundeP Tag WoT Monat Jahr P\n");
+ PDEBUG(DDSP, DEBUG_INFO, "%s\n", tx->data_string);
}
if (second == 59)
symbol = 'm';
- else symbol = ((tx->data_frame >> second) & 1) + '0';
+ else
+ symbol = ((tx->data_frame >> second) & 1) + '0';
PDEBUG(DDSP, DEBUG_DEBUG, "Trasmitting symbol '%c' (Bit %d)\n", symbol, second);
return symbol;
}
+static void rx_symbol(dcf77_t *dcf77, char symbol);
+
+/* encode one minute */
void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
{
+#ifdef DEBUG_LOOP
+ /* mute and speed up by factor 20 */
+ memset(samples, 0, sizeof(*samples) * length);
+ sample_t test_sample[length * 20];
+ samples = test_sample;
+ length *= 20;
+#endif
dcf77_tx_t *tx = &dcf77->tx;
double carrier_phase, test_phase;
int i;
@@ -310,6 +862,9 @@ void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
tx->timestamp += 60;
}
tx->symbol = tx_symbol(dcf77, tx->timestamp, tx->second);
+#ifdef DEBUG_LOOP
+ rx_symbol(dcf77, tx->symbol);
+#endif
}
switch (tx->symbol) {
case '0':
@@ -345,7 +900,146 @@ void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
tx->test_phase = test_phase;
}
-static void rx_frame(uint64_t frame)
+/*
+ * RX part
+ */
+
+/* display weather data from weather frame */
+static void display_weather(uint32_t weather, int minute, int utc_hour)
+{
+ int dataset = ((utc_hour + 2) % 24) * 20 + (minute / 3); /* data sets since 22:00 UTC */
+ int value;
+
+ PDEBUG(DFRAME, DEBUG_INFO, "Decoding weather for dataset %d/480\n", dataset);
+ printf("Received Weather INFO\n");
+ printf("---------------------\n");
+ printf("Time (UTC): %02d:%02d\n", utc_hour, minute);
+ printf("Dataset: %s\n", datasets_0_59[dataset / 60]);
+ value = dataset % 60;
+ printf("Region: %d = %s\n", value, region[value]);
+ if ((dataset / 60) < 7) {
+ value = (weather >> 0) & 0xf;
+ printf("Weather (day): %s = %s\n", show_bits(value, 4), weathers_day[value]);
+ value = (weather >> 4) & 0xf;
+ printf("Weather (night): %s = %s\n", show_bits(value, 4), weathers_night[value]);
+ }
+ if (((dataset / 60) & 1) == 0) {
+ /* even datasets, this is 'Day' data */
+ if (((weather >> 15) & 1) == 0) {
+ value = (weather >> 8) & 0xf;
+ printf("Extreme weather: %s = %s\n", show_bits(value, 4), extremeweathers[value]);
+ } else {
+ value = (weather >> 8) & 0x3;
+ printf("Relative weather: %s = %s\n", show_bits(value, 2), anomaly1[value]);
+ value = (weather >> 10) & 0x3;
+ printf("Sunshine: %s = %s\n", show_bits(value, 2), anomaly1[value]);
+ }
+ value = (weather >> 12) & 0x7;
+ printf("Rain Probability: %s = %s\n", show_bits(value, 3), probabilities[value]);
+ value = (weather >> 15) & 0x1;
+ printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
+ display_weather_temperature("Temperature (day): ", weather);
+ } else {
+ /* odd datasets, this is 'Night' data */
+ if (((weather >> 15) & 1) == 0) {
+ value = (weather >> 8) & 0xf;
+ printf("Wind direction: %s = %s\n", show_bits(value, 4), winddirections[value]);
+ } else {
+ value = (weather >> 8) & 0x3;
+ printf("Relative weather: %s = %s\n", show_bits(value, 2), anomaly1[value]);
+ value = (weather >> 10) & 0x3;
+ printf("Sunshine: %s = %s\n", show_bits(value, 2), anomaly1[value]);
+ }
+ value = (weather >> 12) & 0x7;
+ printf("Wind strength: %s = %s\n", show_bits(value, 3), windstrengths[value]);
+ value = (weather >> 15) & 0x1;
+ printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
+ if ((dataset / 60) < 7)
+ display_weather_temperature("Temperature (night): ", weather);
+ }
+ if ((dataset / 60) == 7) {
+ printf("Dataset: %s\n", 60 + datasets_60_89[(dataset % 60) / 30]);
+ value = 60 + (dataset % 30);
+ printf("Region: %d = %s\n", value, region[value]);
+ value = (weather >> 0) & 0xf;
+ printf("Weather (day): %s = %s\n", show_bits(value, 4), weathers_day[value]);
+ value = (weather >> 4) & 0xf;
+ printf("Weather (night): %s = %s\n", show_bits(value, 4), weathers_night[value]);
+ display_weather_temperature("Temperature: ", weather);
+ }
+}
+
+/* reset weather frame */
+static void rx_weather_reset(dcf77_rx_t *rx)
+{
+ rx->weather_index = 0;
+ rx->weather_cipher = 0;
+ rx->weather_key = 0;
+}
+
+/* receive weather chunk */
+static void rx_weather(dcf77_rx_t *rx, int minute, int hour, int zone, uint64_t frame)
+{
+ int index = (minute + 2) % 3;
+ int utc_hour;
+ int32_t weather;
+
+ if (rx->weather_index == 0 && index != 0) {
+ PDEBUG(DFRAME, DEBUG_INFO, "Skipping weather info chunk, waiting for new start of weather info.\n");
+ return;
+ }
+
+ if (index == 0) {
+ rx_weather_reset(rx);
+ rx->weather_cipher |= (frame >> 2) & 0x3f; /* bit 2-7 */
+ rx->weather_cipher |= (frame >> 3) & 0x0fc0; /* bit 9-14 */
+ rx->weather_index++;
+ if (((frame & 0x0002)) || ((frame & 0x0100)) || !rx->weather_cipher) {
+ PDEBUG(DFRAME, DEBUG_INFO, "There is no weather info in this received minute.\n");
+ rx_weather_reset(rx);
+ return;
+ }
+ PDEBUG(DFRAME, DEBUG_INFO, "Got first chunk of weather info.\n");
+ return;
+ }
+ if (rx->weather_index == 1 && index == 1) {
+ PDEBUG(DFRAME, DEBUG_INFO, "Got second chunk of weather info.\n");
+ rx->weather_cipher |= (frame << 11) & 0x3fff000; /* bit 1-14 */
+ rx->weather_key |= (frame >> 21) & 0x7f;
+ rx->weather_key |= ((frame >> 29) & 0x3f) << 8;
+ rx->weather_key |= ((frame >> 36) & 0x3f) << 16;
+ rx->weather_key |= ((frame >> 45) & 0x1f) << 24;
+ rx->weather_key |= ((frame >> 42) & 0x07) << 29;
+ rx->weather_key |= ((frame >> 50) & 0xff) << 32;
+ rx->weather_index++;
+ return;
+ }
+ if (rx->weather_index == 2 && index == 2) {
+ PDEBUG(DFRAME, DEBUG_INFO, "Got third chunk of weather info.\n");
+ rx->weather_cipher |= (frame << 25) & 0xfffc000000; /* bit 1-14 */
+ weather = weather_decode(rx->weather_cipher, rx->weather_key);
+ if (weather < 0)
+ PDEBUG(DFRAME, DEBUG_NOTICE, "Failed to decrypt weather info, checksum error.\n");
+ else {
+ /* convert hour to UTC */
+ utc_hour = hour - 1;
+ if (zone & 1)
+ utc_hour--;
+ if (utc_hour < 0)
+ utc_hour += 24;
+ /* in index 2 we transmit minute + 3 (next minute), so we substract 3 */
+ display_weather(weather, (minute + 57) % 60, utc_hour);
+ }
+ rx_weather_reset(rx);
+ return;
+ }
+
+ rx_weather_reset(rx);
+ PDEBUG(DFRAME, DEBUG_INFO, "Got weather info chunk out of order, waiting for new start of weather info.\n");
+}
+
+/* decode time from received data */
+static void rx_frame(dcf77_rx_t *rx, uint64_t frame)
{
int zone;
int minute_one, minute_ten, minute = -1;
@@ -428,15 +1122,34 @@ static void rx_frame(uint64_t frame)
PDEBUG(DFRAME, DEBUG_INFO, "Year : %02d\n", year);
}
- if (minute >= 0 && hour >= 0 && day >= 0 && wday >= 0 && month >= 0 && year >= 0)
+ if (minute >= 0 && hour >= 0 && day >= 0 && wday >= 0 && month >= 0 && year >= 0) {
PDEBUG(DDCF77, DEBUG_NOTICE, "The received time is: %s %s %d %02d:%02d:00 %s 20%02d\n", week_day[wday], month_name[month], day, hour, minute, time_zone[zone], year);
- else
+ rx_weather(rx, minute, hour, zone, frame);
+ } else {
PDEBUG(DDCF77, DEBUG_NOTICE, "The received time is invalid!\n");
+ rx_weather_reset(rx);
+ }
}
+/* test routing for test data */
+void rx_frame_test(dcf77_t *dcf77, const char *string)
+{
+ uint64_t frame = 0;
+ int i;
+
+ puts(string);
+ for (i = 0; i < 59; i++) {
+ frame |= (uint64_t)(string[i] & 1) << i;
+ }
+
+ rx_frame(&dcf77->rx, frame);
+}
+
+/* receive one symbol = one second */
static void rx_symbol(dcf77_t *dcf77, char symbol)
{
dcf77_rx_t *rx = &dcf77->rx;
+ double second = -NAN;
PDEBUG(DDSP, DEBUG_DEBUG, "Received symbol '%c'\n", symbol);
@@ -445,33 +1158,48 @@ static void rx_symbol(dcf77_t *dcf77, char symbol)
PDEBUG(DDSP, DEBUG_INFO, "Reception of frame has started\n");
rx->data_receive = 1;
rx->data_index = 0;
+ rx->string_index = 0;
+ second = 0;
}
} else {
if (symbol == 'm') {
if (rx->data_index == 59) {
- rx->data_string[rx->data_index] = '\0';
+ rx->data_string[rx->string_index] = '\0';
rx->data_index = 0;
- PDEBUG(DDSP, DEBUG_INFO, "Received complete frame: %s (0x%016" PRIx64 ")\n", rx->data_string, rx->data_frame);
- rx_frame(rx->data_frame);
+ rx->string_index = 0;
+ PDEBUG(DDSP, DEBUG_INFO, "Received complete frame:\n");
+ PDEBUG(DDSP, DEBUG_INFO, "0 Wetterdaten Info 1 Minute P StundeP Tag WoT Monat Jahr P\n");
+ PDEBUG(DDSP, DEBUG_INFO, "%s\n", rx->data_string);
+ rx_frame(rx, rx->data_frame);
+ second = 0;
} else {
PDEBUG(DDSP, DEBUG_INFO, "Short read, frame too short\n");
rx->data_index = 0;
+ rx->string_index = 0;
+ rx_weather_reset(rx);
}
} else {
if (rx->data_index == 59) {
PDEBUG(DDSP, DEBUG_INFO, "Long read, frame too long\n");
rx->data_receive = 0;
+ rx_weather_reset(rx);
} else {
- rx->data_string[rx->data_index++] = symbol;
+ if (rx->data_index == 1 || rx->data_index == 15 || rx->data_index == 21 || rx->data_index == 29 || rx->data_index == 36 || rx->data_index == 42 || rx->data_index == 45 || rx->data_index == 50)
+ rx->data_string[rx->string_index++] = ' ';
+ rx->data_string[rx->string_index++] = symbol;
+ rx->data_index++;
rx->data_frame >>= 1;
rx->data_frame |= (uint64_t)(symbol & 1) << 58;
+ second = rx->data_index;
}
}
}
+ display_measurements_update(dcf77->dmp_current_second, second, 0.0);
}
//#define DEBUG_SAMPLE
+/* decode radio wave and extract each bit / second */
void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length)
{
dcf77_rx_t *rx = &dcf77->rx;
@@ -481,6 +1209,9 @@ void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length)
display_wave(&dcf77->dispwav, samples, length, 1.0);
+#ifdef DEBUG_LOOP
+ return;
+#endif
if (!rx->enable)
return;
diff --git a/src/dcf77/dcf77.h b/src/dcf77/dcf77.h
index 123e081..d129f27 100644
--- a/src/dcf77/dcf77.h
+++ b/src/dcf77/dcf77.h
@@ -15,7 +15,18 @@ typedef struct dcf77_tx {
int second;
char symbol;
uint64_t data_frame;
+ char data_string[100]; /* 60 digits + spaces + '\0' */
int test_tone;
+ int weather;
+ int weather_day;
+ int weather_night;
+ int extreme;
+ int rain;
+ int wind_dir;
+ int wind_bft;
+ int temperature_day;
+ int temperature_night;
+ uint64_t weather_cipher;
} dcf77_tx_t;
typedef struct dcf77_rx {
@@ -29,9 +40,12 @@ typedef struct dcf77_rx {
int clock_count;
double value_level, value_short, value_long; /* measured values */
int data_receive, data_index;
- char data_string[60]; /* 59 digits + '\0' */
+ char data_string[100]; /* 60 digits + spaces + '\0' */
+ int string_index;
uint64_t data_frame;
- iir_filter_t clock_lp[2]; /* filters received carrier signal */
+ int weather_index;
+ uint64_t weather_cipher;
+ uint64_t weather_key;
} dcf77_rx_t;
typedef struct dcf77 {
@@ -43,6 +57,7 @@ typedef struct dcf77 {
dispmeasparam_t *dmp_input_level;
dispmeasparam_t *dmp_signal_level;
dispmeasparam_t *dmp_signal_quality;
+ dispmeasparam_t *dmp_current_second;
/* wave */
dispwav_t dispwav; /* display wave form */
@@ -52,6 +67,10 @@ int dcf77_init(int _fast_math);
void dcf77_exit(void);
dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone);
void dcf77_destroy(dcf77_t *dcf77);
-void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp);
+void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp, double sub_sec);
void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length);
void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length);
+
+void list_weather(void);
+time_t dcf77_start_weather(time_t timestamp, int region, int offset);
+void dcf77_set_weather(dcf77_t *dcf77, int weather_day, int weather_night, int extreme, int rain, int wind_dir, int wind_bft, int temperature_day, int temperature_night);
diff --git a/src/dcf77/image.c b/src/dcf77/image.c
new file mode 100644
index 0000000..e6ce671
--- /dev/null
+++ b/src/dcf77/image.c
@@ -0,0 +1,16 @@
+#include
+
+const char *aaimage[] = {
+ "",
+ "@W * DCF77 with weather info *",
+ "",
+ " @Y\\__/",
+ " @w_______@Y/ \\__",
+ " @w/ ____\\_@Y/@w_",
+ " \\___/@r10:08:33@w\\",
+ " @B/ @w\\________/",
+ " @B/ @W*@B/ @W*@B/ @W*@B/ @W*@B/",
+ " @W*@B/ @W*@B/ @W*@B/ @W*@B/",
+ "",
+ NULL
+};
diff --git a/src/dcf77/main.c b/src/dcf77/main.c
index 8f7ab45..c9d4dcc 100755
--- a/src/dcf77/main.c
+++ b/src/dcf77/main.c
@@ -27,11 +27,14 @@
#include
#include
#include
+#include
#include "../libdebug/debug.h"
#include "../liboptions/options.h"
#include "../libsample/sample.h"
#include "../libsound/sound.h"
+#include "../libaaimage/aaimage.h"
#include "dcf77.h"
+#include "cities.h"
int num_kanal = 1;
dcf77_t *dcf77 = NULL;
@@ -40,7 +43,17 @@ static const char *dsp_device = "";
static int dsp_samplerate = 192000;
static int dsp_buffer = 50;
static int rx = 0, tx = 0;
-static time_t timestamp = -1;
+static double timestamp = -1;
+static int weather = 0;
+static int weather_day;
+static int weather_night;
+static int extreme;
+static int rain;
+static int wind_dir;
+static int wind_bft;
+static int temperature_day;
+static int temperature_night;
+static int region = -1, region_advance;
static int double_amplitude = 0;
static int test_tone = 0;
static int dsp_interval = 1; /* ms */
@@ -110,8 +123,6 @@ static time_t feierabend_time()
t = get_time();
tm = localtime(&t);
- if (!tm)
- return -1;
tm->tm_hour = 17;
tm->tm_min = 0;
@@ -148,7 +159,7 @@ void print_help(void)
printf(" -R --rx\n");
printf(" Receive time signal\n");
printf(" -F --fake\n");
- printf(" Use given time stamp: .\n");
+ printf(" Use given time stamp: .\n");
printf(" All values have to be numerical. The year must have 4 digits.\n");
printf(" --feierabend\n");
printf(" --end-of-working-day\n");
@@ -156,6 +167,29 @@ void print_help(void)
printf(" --geburtstag\n");
printf(" --birthday\n");
printf(" Use fake time stamp that equals birth of the author.\n");
+ printf(" -W --weather \n");
+ printf(" Send these weather info for all regions / all days.\n");
+ printf(" See -L for infos on values.\n");
+ printf(" weather = 1..15 for common day and night weather.\n");
+ printf(" weather = 1..15,1..15 for specific day and night weather.\n");
+ printf(" extreme = 0..15 for extreme weather conditions.\n");
+ printf(" rain = 0..100 for rain/show probability. (closest is used)\n");
+ printf(" wind dir = N | NE | E | SE | S | SW | W | NW | 0 for wind direction.\n");
+ printf(" wind bft = for wind speed in bft. (closest is used)\n");
+ printf(" temerature = for common min and max temperature.\n");
+ printf(" temerature = , for specific min and max temperature.\n");
+ printf(" --beach-party\n");
+ printf(" Beach weather, equivalent to -W 1 0 0 0 2 35,20\n");
+ printf(" --santa-claus\n");
+ printf(" --muenster-2005\n");
+ printf(" Deep snow, equivalent to -W 7 1 100 E 3 1,-1\n");
+ printf(" -A --at-region \n");
+ printf(" Alter time, so that the weather of the given region is transmitted.\n");
+ printf(" To allow the receiver to sync, give time to advance in minutes.\n");
+ printf(" -L --list\n");
+ printf(" List all regions / weather values.\n");
+ printf(" -C --city \n");
+ printf(" Search for city (case insensitive) and display its region code.\n");
printf(" -D --double-amplitude\n");
printf(" Transmit with double amplitude by using differential stereo output.\n");
printf(" --test-tone\n");
@@ -173,8 +207,11 @@ void print_help(void)
#define OPT_F2 1002
#define OPT_G1 1003
#define OPT_G2 1004
-#define OPT_TEST_TONE 1005
-#define OPT_FAST_MATH 1006
+#define OPT_BEACH 1005
+#define OPT_SANTA 1006
+#define OPT_MUENSTER 1007
+#define OPT_TEST_TONE 1008
+#define OPT_FAST_MATH 1009
static void add_options(void)
{
@@ -190,15 +227,25 @@ static void add_options(void)
option_add(OPT_F2, "end-of-working-day", 0);
option_add(OPT_G1, "geburtstag", 0);
option_add(OPT_G2, "birthday", 0);
- option_add(OPT_TEST_TONE, "test-tone", 0);
+ option_add('W', "weather", 6);
+ option_add(OPT_BEACH, "beach-party", 0);
+ option_add(OPT_SANTA, "santa-claus", 0);
+ option_add(OPT_MUENSTER, "muenster-2005", 0);
+ option_add('A', "at-region", 2);
+ option_add('L', "list", 0);
+ option_add('C', "city", 1);
option_add('D', "double-amplitude", 0);
+ option_add(OPT_TEST_TONE, "test-tone", 0);
option_add('r', "realtime", 1);
option_add(OPT_FAST_MATH, "fast-math", 0);
}
+static const char *wind_dirs[8] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };
+
static int handle_options(int short_option, int argi, char **argv)
{
- int rc;
+ char *string, *string1;
+ int rc, i;
switch (short_option) {
case 'h':
@@ -233,7 +280,6 @@ static int handle_options(int short_option, int argi, char **argv)
break;
case 'F':
timestamp = parse_time(argv + argi);
- printf("%ld\n",timestamp);
if (timestamp < 0) {
fprintf(stderr, "Given time stamp is invalid, please use -h for help.\n");
return -EINVAL;
@@ -247,6 +293,79 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_G2:
timestamp = 115099200 - 70;
break;
+ case 'W':
+ if (weather) {
+no_multiple_weathers:
+ fprintf(stderr, "You cannot define more than one weather situation.\n");
+ return -EINVAL;
+ }
+ weather = 1;
+ string = options_strdup(argv[argi++]);
+ string1 = strsep(&string, ",");
+ weather_day = atoi(string1);
+ if (string)
+ weather_night = atoi(string);
+ else
+ weather_night = weather_day;
+ extreme = atoi(argv[argi++]);
+ rain = atoi(argv[argi++]);
+ /* if wind is not found, wind 8 (changable) is selected */
+ string = options_strdup(argv[argi++]);
+ for (i = 0; i < 8; i++) {
+ if (!strcasecmp(string, wind_dirs[i]))
+ break;
+ }
+ wind_dir = i;
+ wind_bft = atoi(argv[argi++]);
+ string = options_strdup(argv[argi++]);
+ string1 = strsep(&string, ",");
+ temperature_day = atoi(string1);
+ if (string)
+ temperature_night = atoi(string);
+ else
+ temperature_night = temperature_day;
+ break;
+ case OPT_BEACH:
+ if (weather)
+ goto no_multiple_weathers;
+ weather = 1;
+ weather_day = 1;
+ weather_night = 1;
+ extreme = 0;
+ rain = 0;
+ wind_dir = 8;
+ wind_bft = 2;
+ temperature_day = 35;
+ temperature_night = 20; /* tropical night >= 20 */
+ break;
+ case OPT_SANTA:
+ case OPT_MUENSTER:
+ if (weather)
+ goto no_multiple_weathers;
+ weather = 1;
+ weather_day = 7;
+ weather_night = 7;
+ extreme = 0;
+ rain = 100;
+ wind_dir = 6;
+ wind_bft = 3;
+ temperature_day = 1;
+ temperature_night = -1; /* freezing a little */
+ break;
+ case 'A':
+ region = atoi(argv[argi++]);
+ if (region < 0 || region > 89) {
+ fprintf(stderr, "Given region number is is invalid, please use -L for list of valid regions.\n");
+ return -EINVAL;
+ }
+ region_advance = atoi(argv[argi++]);
+ break;
+ case 'L':
+ list_weather();
+ return 0;
+ case 'C':
+ display_city(argv[argi++]);
+ return 0;
case OPT_TEST_TONE:
test_tone = 1;
break;
@@ -407,8 +526,20 @@ int main(int argc, char *argv[])
fprintf(stderr, "Failed to create \"DCF77\" instance. Quitting!\n");
goto error;
}
+ if (weather)
+ dcf77_set_weather(dcf77, weather_day, weather_night, extreme, rain, wind_dir, wind_bft, temperature_day, temperature_night);
- printf("\n");
+ /* no time stamp given, so we use our clock */
+ if (tx) {
+ if (timestamp < 0 && region < 0)
+ printf("No alternative time given, so you might not notice the difference between our transmission and the real DCF77 transmission.\n");
+ if (timestamp < 0)
+ timestamp = get_time();
+ if (region >= 0 && weather)
+ timestamp = dcf77_start_weather((time_t)timestamp, region, region_advance);
+ }
+
+ print_aaimage();
printf("DCF77 ready.\n");
/* prepare terminal */
@@ -437,7 +568,7 @@ int main(int argc, char *argv[])
soundif_start();
if (tx)
- dcf77_tx_start(dcf77, timestamp);
+ dcf77_tx_start(dcf77, (time_t)timestamp, fmod(timestamp, 1.0));
while (!quit) {
int w;
diff --git a/src/dcf77/weather.c b/src/dcf77/weather.c
new file mode 100644
index 0000000..41b7824
--- /dev/null
+++ b/src/dcf77/weather.c
@@ -0,0 +1,582 @@
+
+/* based on code found at:
+https://github.com/FroggySoft/AlarmClock/blob/master/dcf77.cpp
+https://github.com/tobozo/esp32-dcf77-weatherman/blob/master/dcf77.cpp
+*/
+
+#include
+#include
+#include
+#include "../libdebug/debug.h"
+#include "weather.h"
+
+/// Container zum Konvertieren zwischen 4 Bytes und Uint.
+union ByteUInt {
+ struct {
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t Byte0;
+ uint8_t Byte1;
+ uint8_t Byte2;
+ uint8_t Byte3;
+# elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t Byte3;
+ uint8_t Byte2;
+ uint8_t Byte1;
+ uint8_t Byte0;
+# else
+#error unsupported bitorder, please fix
+# endif
+ } s;
+ uint32_t FullUint;
+};
+
+/// bit pattern for 0D,0E from 0B-0D
+static const uint32_t mUintArrBitPattern12[12] = {
+ 0x80000, //0b10000000000000000000 / 0D.3
+ 0x00010, //0b00000000000000010000 / 0B.4
+ 0x00008, //0b00000000000000001000 / 0B.3
+ 0x00100, //0b00000000000100000000 / 0C.0
+ 0x00080, //0b00000000000010000000 / 0B.7
+ 0x01000, //0b00000001000000000000 / 0C.4
+ 0x00800, //0b00000000100000000000 / 0C.3
+ 0x10000, //0b00010000000000000000 / 0D.0
+ 0x08000, //0b00001000000000000000 / 0C.7
+ 0x00001, //0b00000000000000000001 / 0B.0
+ 0x00000, //0b00000000000000000000 / xxxx
+ 0x00000 //0b00000000000000000000 / xxxx
+};
+
+/// 12-15 from 16-19 (time)
+static const uint32_t mUintArrBitPattern30_1[30] = {
+ 0x00000200, //0b00000000000000000000001000000000 / 17.1
+ 0x00000020, //0b00000000000000000000000000100000 / 16.5
+ 0x02000000, //0b00000010000000000000000000000000 / 19.1
+ 0x00000000, //0b00000000000000000000000000000000 / 1A.3
+ 0x00000000, //0b00000000000000000000000000000000 / 1A.5
+ 0x00000080, //0b00000000000000000000000010000000 / 16.7
+ 0x40000000, //0b01000000000000000000000000000000 / 19.6
+ 0x01000000, //0b00000001000000000000000000000000 / 19.0
+
+ 0x04000000, //0b00000100000000000000000000000000 / 19.2
+ 0x00000000, //0b00000000000000000000000000000000 / 1A.4
+ 0x00010000, //0b00000000000000010000000000000000 / 18.0
+ 0x00000000, //0b00000000000000000000000000000000 / 1A.2
+ 0x00400000, //0b00000000010000000000000000000000 / 18.6
+ 0x00000010, //0b00000000000000000000000000010000 / 16.4
+ 0x00200000, //0b00000000001000000000000000000000 / 18.5
+ 0x00080000, //0b00000000000010000000000000000000 / 18.3
+
+ 0x00004000, //0b00000000000000000100000000000000 / 17.6
+ 0x00000000, //0b00000000000000000000000000000000 / 1A.6
+ 0x00020000, //0b00000000000000100000000000000000 / 18.1
+ 0x00100000, //0b00000000000100000000000000000000 / 18.4
+ 0x00008000, //0b00000000000000001000000000000000 / 17.7
+ 0x00000040, //0b00000000000000000000000001000000 / 16.6
+ 0x00001000, //0b00000000000000000001000000000000 / 17.4
+ 0x00000400, //0b00000000000000000000010000000000 / 17.2
+
+ 0x00000001, //0b00000000000000000000000000000001 / 16.0
+ 0x80000000, //0b10000000000000000000000000000000 / 19.7
+ 0x00000008, //0b00000000000000000000000000001000 / 16.3
+ 0x00000002, //0b00000000000000000000000000000010 / 16.1
+ 0x00040000, //0b00000000000001000000000000000000 / 18.2
+ 0x10000000 //0b00010000000000000000000000000000 / 19.4
+};
+
+/// bit pattern for 12-15 from 1A (time2)
+static const uint32_t mUintArrBitPattern30_2[30] = {
+ 0x00, //0b00000000, /* 17.1
+ 0x00, //0b00000000, /* 16.5
+ 0x00, //0b00000000, /* 19.1
+ 0x08, //0b00001000, /* 1A.3
+ 0x20, //0b00100000, /* 1A.5
+ 0x00, //0b00000000, /* 16.7
+ 0x00, //0b00000000, /* 19.6
+ 0x00, //0b00000000, /* 19.0
+
+ 0x00, //0b00000000, /* 19.2
+ 0x10, //0b00010000, /* 1A.4
+ 0x00, //0b00000000, /* 18.0
+ 0x04, //0b00000100, /* 1A.2
+ 0x00, //0b00000000, /* 18.6
+ 0x00, //0b00000000, /* 16.4
+ 0x00, //0b00000000, /* 18.5
+ 0x00, //0b00000000, /* 18.3
+
+ 0x00, //0b00000000, /* 17.6
+ 0x40, //0b01000000, /* 1A.6
+ 0x00, //0b00000000, /* 18.1
+ 0x00, //0b00000000, /* 18.4
+ 0x00, //0b00000000, /* 17.7
+ 0x00, //0b00000000, /* 16.6
+ 0x00, //0b00000000, /* 17.4
+ 0x00, //0b00000000, /* 17.2
+
+ 0x00, //0b00000000, /* 16.0
+ 0x00, //0b00000000, /* 19.7
+ 0x00, //0b00000000, /* 16.3
+ 0x00, //0b00000000, /* 16.1
+ 0x00, //0b00000000, /* 18.2
+ 0x00 //0b00000000, /* 19.4
+};
+
+/// 12-14 from 1C-1E (result from F)
+static const uint32_t mUintArrBitPattern20[20] = {
+ 0x000004, //0b000000000000000000000100 / 1C.2
+ 0x002000, //0b000000000010000000000000 / 1E.5
+ 0x008000, //0b000000001000000000000000 / 1E.7
+ 0x400000, //0b010000000000000000000000 / 1D.6
+ 0x000100, //0b000000000000000100000000 / 1E.0
+ 0x100000, //0b000100000000000000000000 / 1D.4
+ 0x000400, //0b000000000000010000000000 / 1E.2
+ 0x800000, //0b100000000000000000000000 / 1D.7
+
+ 0x040000, //0b000001000000000000000000 / 1D.2
+ 0x020000, //0b000000100000000000000000 / 1D.1
+ 0x000008, //0b000000000000000000001000 / 1C.3
+ 0x000200, //0b000000000000001000000000 / 1E.1
+ 0x004000, //0b000000000100000000000000 / 1E.6
+ 0x000002, //0b000000000000000000000010 / 1C.1
+ 0x001000, //0b000000000001000000000000 / 1E.4
+ 0x080000, //0b000010000000000000000000 / 1D.3
+
+ 0x000800, //0b000000000000100000000000 / 1E.3
+ 0x200000, //0b001000000000000000000000 / 1D.5
+ 0x010000, //0b000000010000000000000000 / 1D.0
+ 0x000001 //0b000000000000000000000001 / 1C.0
+};
+
+/// bit pattern for 12-15 from 16-19 (1/3)
+static const uint64_t mByteArrLookupTable1C_1[8] = {
+ 0xBB0E22C573DFF76D, 0x90E9A1381C844A56,
+ 0x648D280BD1BA9352, 0x1CC5A7F0E97F364E,
+ 0xC1773DB3AAE00C6F, 0x1488F62BD2995E45,
+ 0x1F7096D3B30BFCEE, 0x8142CA34A5582967
+};
+
+/// bit pattern for 12-15 from 16-19 (2/3)
+static const uint64_t mByteArrLookupTable1C_2[8] = {
+ 0xAB3DFC7465E60E4F, 0x9711D85983C2BA20,
+ 0xC51BD2584937017D, 0x93FAE02F66B4AC8E,
+ 0xB7CC43FF5866EB35, 0x822A99DD007114AE,
+ 0x4EB1F7701852AA9F, 0xD56BCC3D0483E926
+};
+
+/// bit pattern for 12-15 from 16-19 (3/3)
+static const uint64_t mByteArrLookupTable1C_3[8] = {
+ 0x0A02000F06070D08, 0x030C0B050901040E,
+ 0x0209050D0C0E0F08, 0x06070B01000A0403,
+ 0x08000D0F010C0306, 0x0B0409050A07020E,
+ 0x030D000C09060F0B, 0x010E080A02070405
+};
+
+/// Container, which contains all former global vars
+typedef struct DataContainer {
+ /// Registers R12 to R15
+ union ByteUInt mByteUint1;
+ /// Registers R08 to R0A
+ union ByteUInt mByteUint2;
+ /// Registers R0B to R0E
+ union ByteUInt mByteUint3;
+ /// Registers R1C to R1E
+ union ByteUInt mByteUint4;
+
+ uint8_t mByteUpperTime2;//, mByteR1B;
+ uint32_t mUintLowerTime;
+} DataContainer_t;
+
+static int32_t GetWeatherFromPlain(uint8_t *PlainBytes)
+{
+ uint32_t result;
+ uint32_t checkSum;
+
+ checkSum = PlainBytes[2] & 0x0f;
+ checkSum <<= 8;
+ checkSum |= PlainBytes[1];
+ checkSum <<= 4;
+ checkSum |= PlainBytes[0] >> 4;
+ if (checkSum != 0x2501)
+ return -1;
+
+ result = PlainBytes[0] & 0x0f;
+ result <<= 8;
+ result |= PlainBytes[4];
+ result <<= 8;
+ result |= PlainBytes[3];
+ result <<= 4;
+ result |= PlainBytes[2] >> 4;
+
+ return result;
+}
+
+static uint8_t *GetPlainFromWeather(uint32_t weather)
+{
+ static uint8_t result[5];
+ weather <<= 4;
+ result[1] = 0x50;
+ result[2] = (weather & 0xf0) | 0x02;
+ weather >>= 8;
+ result[3] = weather & 0xff;
+ weather >>= 8;
+ result[4] = weather & 0xff;
+ weather >>= 8;
+ result[0] = (weather & 0x0f) | 0x10;
+ return result;
+}
+
+static void CopyTimeToByteUint(uint8_t *data, uint8_t *key, DataContainer_t *container)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ container->mUintLowerTime <<= 8;
+ container->mUintLowerTime |= key[3 - i];
+ }
+ container->mByteUpperTime2 = key[4];
+
+ // copy R
+ container->mByteUint3.s.Byte0 = data[2];
+ container->mByteUint3.s.Byte1 = data[3];
+ container->mByteUint3.s.Byte2 = data[4];
+ container->mByteUint3.FullUint >>= 4;
+
+ // copy L
+ container->mByteUint2.s.Byte0 = data[0];
+ container->mByteUint2.s.Byte1 = data[1];
+ container->mByteUint2.s.Byte2 = (uint8_t)(data[2] & 0x0F);
+}
+
+static void ShiftTimeRight(int round, DataContainer_t *container)
+{
+ int count;
+ uint8_t tmp;
+
+ if ((round == 16) || (round == 8) || (round == 7) || (round == 3))
+ count = 2;
+ else
+ count = 1;
+
+ while (count-- != 0)
+ {
+ tmp = 0;
+ if ((container->mUintLowerTime & 0x00100000) != 0) // save time bit 20
+ tmp = 1;
+
+ container->mUintLowerTime &= 0xFFEFFFFF;
+ if ((container->mUintLowerTime & 1) != 0)
+ container->mUintLowerTime |= 0x00100000; // copy time bit 0 to time bit 19
+ container->mUintLowerTime >>= 1; // time >>= 1
+
+ if ((container->mByteUpperTime2 & 1) != 0)
+ container->mUintLowerTime |= 0x80000000;
+ container->mByteUpperTime2 >>= 1;
+ if (tmp != 0)
+ container->mByteUpperTime2 |= 0x80; // insert time bit 20 to time bit 39
+ }
+
+}
+
+static void ShiftTimeLeft(int round, DataContainer_t *container)
+{
+ int count;
+ uint8_t tmp;
+
+ if ((round == 16) || (round == 8) || (round == 7) || (round == 3))
+ count = 2;
+ else
+ count = 1;
+
+ while (count-- != 0)
+ {
+ tmp = 0;
+ if ((container->mByteUpperTime2 & 0x80) != 0)
+ tmp = 1;
+ container->mByteUpperTime2 <<= 1;
+
+ if ((container->mUintLowerTime & 0x80000000) != 0)
+ container->mByteUpperTime2 |= 1;
+
+ container->mUintLowerTime <<= 1;
+ if ((container->mUintLowerTime & 0x00100000) != 0)
+ container->mUintLowerTime |= 1;
+
+ container->mUintLowerTime &= 0xFFEFFFFF;
+ if (tmp != 0)
+ container->mUintLowerTime |= 0x00100000;
+ }
+}
+
+static void ExpandR(DataContainer_t *container)
+{
+ uint32_t tmp;
+ int i;
+
+ container->mByteUint3.FullUint &= 0x000FFFFF; // clear 0D(4-7),0E
+ tmp = 0x00100000; // and set bits form 0B-0D(0-3)
+ for (i = 0; i < 12; i++)
+ {
+ if ((container->mByteUint3.FullUint & mUintArrBitPattern12[i]) != 0)
+ container->mByteUint3.FullUint |= tmp;
+ tmp <<= 1;
+ }
+}
+
+static void ExpandL(DataContainer_t *container)
+{
+ uint32_t tmp;
+ int i;
+
+ container->mByteUint2.FullUint &= 0x000FFFFF; // clear 0D(4-7),0E
+ tmp = 0x00100000; // and set bits form 0B-0D(0-3)
+ for (i = 0; i < 12; i++)
+ {
+ if ((container->mByteUint2.FullUint & mUintArrBitPattern12[i]) != 0)
+ container->mByteUint2.FullUint |= tmp;
+ tmp <<= 1;
+ }
+}
+
+static void CompressKey(DataContainer_t *container)
+{
+ uint32_t tmp;
+ int i;
+
+ container->mByteUint1.FullUint = 0; // clear 12-15
+ tmp = 0x00000001; // and set bits from 16-1A (time)
+ for (i = 0; i < 30; i++)
+ {
+ if ((container->mUintLowerTime & mUintArrBitPattern30_1[i]) != 0 || (container->mByteUpperTime2 & mUintArrBitPattern30_2[i]) != 0)
+ container->mByteUint1.FullUint |= tmp;
+ tmp <<= 1;
+ }
+}
+
+static void DoSbox(DataContainer_t *container)
+{
+ uint8_t tmp, helper; //mByteR1B;
+ int i;
+
+ helper = container->mByteUint1.s.Byte3; // R1B = R15;
+ container->mByteUint1.s.Byte3 = container->mByteUint1.s.Byte2; // R15 = R14
+
+ // INNER LOOP
+ for (i = 5; i > 0; i--)
+ {
+ if ((i & 1) == 0) // round 4,2
+ {
+ tmp = (uint8_t)(container->mByteUint1.s.Byte0 >> 4); // swap R12
+ tmp |= (uint8_t)((container->mByteUint1.s.Byte0 & 0x0f) << 4);
+ container->mByteUint1.s.Byte0 = tmp;
+ }
+ container->mByteUint1.s.Byte3 &= 0xF0; // set R1C
+ tmp = (uint8_t)((container->mByteUint1.s.Byte0 & 0x0F) | container->mByteUint1.s.Byte3);
+
+ if ((i & 4) != 0)
+ tmp = mByteArrLookupTable1C_1[(tmp & 0x38) >> 3] >> (56 - (tmp & 0x07) * 8);
+
+ if ((i & 2) != 0)
+ tmp = mByteArrLookupTable1C_2[(tmp & 0x38) >> 3] >> (56 - (tmp & 0x07) * 8);
+
+ else if (i == 1)
+ tmp = mByteArrLookupTable1C_3[(tmp & 0x38) >> 3] >> (56 - (tmp & 0x07) * 8);
+
+ if ((i & 1) != 0)
+ container->mByteUint4.s.Byte0 = (uint8_t)(tmp & 0x0F);
+ else
+ container->mByteUint4.s.Byte0 |= (uint8_t)(tmp & 0xF0);
+
+ if ((i & 1) == 0) // copy 14->13->12, 1C->1E->1D
+ {
+ tmp = container->mByteUint1.s.Byte3;
+ container->mByteUint1.FullUint >>= 8;
+ container->mByteUint1.s.Byte3 = tmp;
+ container->mByteUint4.FullUint <<= 8;
+ }
+
+ container->mByteUint1.s.Byte3 >>= 1; // rotate R1B>R15 twice
+ if ((helper & 1) != 0)
+ container->mByteUint1.s.Byte3 |= 0x80;
+ helper >>= 1;
+
+ container->mByteUint1.s.Byte3 >>= 1;
+ if ((helper & 1) != 0)
+ container->mByteUint1.s.Byte3 |= 0x80;
+ helper >>= 1;
+ } // end of inner loop
+}
+
+static void DoPbox(DataContainer_t *container)
+{
+ uint32_t tmp;
+ int i;
+
+ container->mByteUint1.FullUint = 0xFF000000; // clear 12-14
+ tmp = 0x00000001; // and set bits from 1C-1E (result from F)
+ for (i = 0; i < 20; i++)
+ {
+ if ((container->mByteUint4.FullUint & mUintArrBitPattern20[i]) != 0)
+ container->mByteUint1.FullUint |= tmp;
+ tmp <<= 1;
+ }
+}
+
+/* modified DES decrypt using strings */
+static uint8_t *Decrypt(uint8_t *cipher, uint8_t *key)
+{
+ DataContainer_t container;
+ int i;
+
+ static uint8_t plain[5];
+ CopyTimeToByteUint(cipher, key, &container);
+
+ // OUTER LOOP 1
+ for (i = 16; i > 0; i--)
+ {
+ ShiftTimeRight(i, &container);
+ ExpandR(&container);
+ CompressKey(&container);
+
+ // expR XOR compr.Key
+ container.mByteUint1.FullUint ^= container.mByteUint3.FullUint; // 12-15 XOR 0B-0E
+ container.mByteUint3.s.Byte2 &= 0x0F; // clear 0D(4-7)
+
+ DoSbox(&container);
+ DoPbox(&container);
+
+ // L XOR P-Boxed Round-Key (L')
+ container.mByteUint1.FullUint ^= container.mByteUint2.FullUint;
+
+ // L = R
+ container.mByteUint2.FullUint = container.mByteUint3.FullUint & 0x00FFFFFF;
+
+ // R = L'
+ container.mByteUint3.FullUint = container.mByteUint1.FullUint & 0x00FFFFFF;
+ } // end of outer loop 1
+
+ container.mByteUint3.FullUint <<= 4;
+ container.mByteUint2.s.Byte2 &= 0x0F;
+ container.mByteUint2.s.Byte2 |= (uint8_t)(container.mByteUint3.s.Byte0 & 0xF0);
+
+ plain[0] = container.mByteUint2.s.Byte0;
+ plain[1] = container.mByteUint2.s.Byte1;
+ plain[2] = container.mByteUint2.s.Byte2;
+ plain[3] = container.mByteUint3.s.Byte1;
+ plain[4] = container.mByteUint3.s.Byte2;
+
+ return plain;
+}
+
+/* modified DES encrypt using strings */
+static uint8_t *Encrypt(uint8_t *plain, uint8_t *key)
+{
+ static uint8_t cipher[5];
+ DataContainer_t container;
+ int i;
+
+ CopyTimeToByteUint(plain, key, &container);
+
+ // OUTER LOOP 1
+ for (i = 1; i < 17; i++)
+ {
+ ExpandL(&container);
+ CompressKey(&container);
+
+ // expR XOR compr.Key
+ container.mByteUint1.FullUint ^= container.mByteUint2.FullUint; // L' XOR compr.Key
+ container.mByteUint3.s.Byte2 &= 0x0F; // clear 0D(4-7)
+
+ DoSbox(&container);
+ DoPbox(&container);
+
+ // L XOR P-Boxed Round-Key (L')
+ container.mByteUint1.FullUint ^= container.mByteUint3.FullUint;
+ // L = R
+ container.mByteUint3.FullUint = container.mByteUint2.FullUint & 0x00FFFFFF;
+ // R = L'
+ container.mByteUint2.FullUint = container.mByteUint1.FullUint & 0x00FFFFFF;
+
+ ShiftTimeLeft(i, &container);
+ } // end of outer loop 1
+
+ container.mByteUint3.FullUint <<= 4;
+ container.mByteUint2.s.Byte2 &= 0x0F;
+ container.mByteUint2.s.Byte2 |= (uint8_t)(container.mByteUint3.s.Byte0 & 0xF0);
+
+ cipher[0] = container.mByteUint2.s.Byte0;
+ cipher[1] = container.mByteUint2.s.Byte1;
+ cipher[2] = container.mByteUint2.s.Byte2;
+ cipher[3] = container.mByteUint3.s.Byte1;
+ cipher[4] = container.mByteUint3.s.Byte2;
+
+ return cipher;
+}
+
+//#define DEBUG_CIPER
+
+/* decode given crypted frame and key
+ * return the weather info or -1 on checksum error
+ */
+int32_t weather_decode(uint64_t cipher, uint64_t key)
+{
+ uint8_t CipherBytes[5];
+ uint8_t KeyBytes[5];
+ uint8_t *PlainBytes;
+ int32_t weather;
+ int i;
+
+ for (i = 0; i < 5; i++)
+ CipherBytes[i] = cipher >> (i * 8);
+
+ for (i = 0; i < 5; i++)
+ KeyBytes[i] = key >> (i * 8);
+
+ PlainBytes = Decrypt(CipherBytes, KeyBytes);
+
+ weather = GetWeatherFromPlain(PlainBytes);
+
+#ifdef DEBUG_CIPER
+ printf("cipher=%s\n", debug_hex(CipherBytes, 5));
+ printf("key =%s\n", debug_hex(KeyBytes, 5));
+ printf("plain =%s\n", debug_hex(PlainBytes, 5));
+ if (weather < 0)
+ printf("weather=error\n");
+ else
+ printf("weather=%06x\n", weather);
+
+ weather_encode(weather, key);
+#endif
+
+ return weather;
+}
+
+/* encode given weather info and key
+ * return crypted frame
+ */
+uint64_t weather_encode(uint32_t weather, uint64_t key)
+{
+ uint8_t KeyBytes[5];
+ uint8_t *PlainBytes;
+ uint8_t *CipherBytes;
+ uint64_t cipher = 0;
+ int i;
+
+ PlainBytes = GetPlainFromWeather(weather);
+
+ for (i = 0; i < 5; i++)
+ KeyBytes[i] = key >> (i * 8);
+
+ CipherBytes = Encrypt(PlainBytes, KeyBytes);
+
+#ifdef DEBUG_CIPER
+ printf("plain =%s\n", debug_hex(PlainBytes, 5));
+ printf("key =%s\n", debug_hex(KeyBytes, 5));
+ printf("cipher=%s\n", debug_hex(CipherBytes, 5));
+#endif
+
+ for (i = 0; i < 5; i++)
+ cipher |= (uint64_t)(CipherBytes[i]) << (i * 8);
+
+ return cipher;
+}
+
diff --git a/src/dcf77/weather.h b/src/dcf77/weather.h
new file mode 100644
index 0000000..1daef97
--- /dev/null
+++ b/src/dcf77/weather.h
@@ -0,0 +1,3 @@
+
+int32_t weather_decode(uint64_t cipher, uint64_t key);
+uint64_t weather_encode(uint32_t weather, uint64_t key);