DCF77: Add weather info decoding and coding

This commit is contained in:
Andreas Eversberg 2022-11-05 10:01:45 +01:00
parent 5ae64a2712
commit 616a5e2820
11 changed files with 2167 additions and 45 deletions

2
README
View File

@ -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

View File

@ -132,6 +132,7 @@ Additional features:
<li>POCSAG (paging system)</li>
<li>Golay / GSC (paging system)</li>
<li>5-Ton-Ruf (firefighter's pagers and siren control)</li>
<li>DCF77 The German longwave time signal transmitter/receiver</li>
</ul>
</td></tr></table></center>

View File

@ -6,6 +6,9 @@ bin_PROGRAMS = \
dcf77_SOURCES = \
dcf77.c \
weather.c \
cities.c \
image.c \
main.c
dcf77_LDADD = \
$(COMMON_LA) \

633
src/dcf77/cities.c Normal file
View File

@ -0,0 +1,633 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#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);
}
}

3
src/dcf77/cities.h Normal file
View File

@ -0,0 +1,3 @@
void display_city(const char *search);

View File

@ -1,5 +1,5 @@
/* implementation of DCF77 transmitter and receiver
/* implementation of DCF77 transmitter and receiver, including weather
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
@ -28,6 +28,7 @@
#include <math.h>
#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(&timestamp);
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(&timestamp);
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(&timestamp);
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(&timestamp);
}
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;

View File

@ -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);

16
src/dcf77/image.c Normal file
View File

@ -0,0 +1,16 @@
#include <stdio.h>
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
};

View File

@ -27,11 +27,14 @@
#include <termios.h>
#include <sched.h>
#include <time.h>
#include <math.h>
#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: <year> <month> <day> <hour> <min< <sec>.\n");
printf(" Use given time stamp: <year> <month> <day> <hour> <min> <sec>.\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 <weather> <extreme> <rain> <wind dir> <wind bft> <temperature>\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 = <bft> for wind speed in bft. (closest is used)\n");
printf(" temerature = <celsius> for common min and max temperature.\n");
printf(" temerature = <celsius>,<celsius> 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 <region> <advance minutes>\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 <name fragment>\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;

582
src/dcf77/weather.c Normal file
View File

@ -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 <stdio.h>
#include <stdint.h>
#include <endian.h>
#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;
}

3
src/dcf77/weather.h Normal file
View File

@ -0,0 +1,3 @@
int32_t weather_decode(uint64_t cipher, uint64_t key);
uint64_t weather_encode(uint32_t weather, uint64_t key);