DCF77 Time Signal Simulator with RTAI 3.2 in hard real time
The DCF-77 Time Signal broadcasted from Germany is a precision atomic clock signal.
Many clocks and wrist watches (Funkuhr, Funkarmbanduhr) use this signal for synchronization.
Here is a hard real time DCF77 simulator, which produces a DCF-77 signal on the
parallel port data pins. The time sent via the parallel port is the local time
of the machine.
DCF77 Time Code Details
- dcf77.c is the RTAI3.2/LXRT version in hard real time
- dcf77_soft.c is the regular GNU/Linux version: make sure you
don't do any CPU or I/O intensive stuff while running the non-RTAI version, otherwise the time signal
might be currupted
This simulator is not meant for generating accurate time, but rather
to test equipment like embedded hardware etc. which requires a DCF-77 signal
without having to use a real DCF77 receiver.
Compile dcf77.c with the Makefile and run it with "run.sh".
dcf77.c
/* DCF77 Simulator for RTAI 3.2
A DCF77 time signal is produced on the parallel port data pins
Data on pins 2-9 (D0-D7)
GND on pins 18-25
Very handy for testing embedded hardware which needs DCF77
(C) 2005 www.captain.at */
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <rtai_lxrt.h>
#include <rtai_sem.h>
#include <rtai_usi.h>
#include <sys/io.h>
#define BASEPORT 0x378
static SEM *dspsem;
static volatile int end_dcf77 = 1;
static volatile int end = 0;
#define PERIOD 100000000 // 100ms
void sendone(void) {
int t;
outb_p(255, BASEPORT);
rt_task_wait_period(); // 1 = 200ms
rt_task_wait_period();
outb_p(0, BASEPORT);
for (t = 1; t <= 8; t++) { rt_task_wait_period(); }
}
void sendzero(void) {
int t;
outb_p(255, BASEPORT);
rt_task_wait_period(); // 0 = 100ms
outb_p(0, BASEPORT);
for (t = 1; t <= 9; t++) { rt_task_wait_period(); }
}
unsigned int Convert2PacketBCD(unsigned int value) {
unsigned int result;
result = value % 10;
result += (value - result) / 10 * 0x10;
return result;
}
static void *dcf77_generator(void *args) {
RT_TASK *handler;
RTIME period;
int i, min, hour, day, wday, month, year;
int now_min, now_hour, now_day, now_wday, now_month, now_year;
time_t now;
struct tm *now_tm;
unsigned int parity_min, parity_hour, parity_date;
if (!(handler = rt_task_init_schmod(nam2num("DCFHLR"),
0, 0, 0, SCHED_FIFO, 0xF))) {
printf("CANNOT INIT HANDLER TASK > DCFHLR <\n");
exit(1);
}
// Get the current date/time before we go to hard real time
// Those are SYSCALLs and would break hard real time
now = time (NULL);
now_tm = localtime (&now);
now_min = now_tm->tm_min;
now_hour = now_tm->tm_hour;
now_day = now_tm->tm_mday;
now_wday = now_tm->tm_wday;
now_month = now_tm->tm_mon + 1;
now_year = now_tm->tm_year - 100;
min = Convert2PacketBCD(now_min);
hour = Convert2PacketBCD(now_hour);
day = Convert2PacketBCD(now_day);
wday = Convert2PacketBCD(now_wday);
month = Convert2PacketBCD(now_month);
year = Convert2PacketBCD(now_year);
rt_allow_nonroot_hrt();
mlockall(MCL_CURRENT | MCL_FUTURE);
rt_make_hard_real_time();
end_dcf77 = 0;
rt_set_oneshot_mode();
start_rt_timer(0);
period = nano2count(PERIOD);
rt_task_make_periodic(handler, rt_get_time() + period, period);
while ( !end_dcf77 ) {
rt_sem_signal(dspsem);
outb_p(0, BASEPORT);
// generate 20 zeros
for (i = 1; i <= 20; i++ ) {
sendzero();
if (end_dcf77) break;
}
// send one start bit
sendone();
// minute
parity_min = 0;
for (i = 1; i <= 7; i++) {
if (min & 1) {
sendone();
parity_min = parity_min ^ 1;
} else sendzero();
min = min >> 1;
}
if (end_dcf77) break;
// minute_parity
if (parity_min) { sendone(); } else { sendzero(); }
// hour
parity_hour = 0;
for (i = 1; i <= 6; i++) {
if (hour & 1) {
sendone();
parity_hour = parity_hour ^ 1;
} else sendzero();
hour = hour >> 1;
}
// hour_parity
if (parity_hour) { sendone(); } else { sendzero(); }
if (end_dcf77) break;
parity_date = 0;
// day
for (i = 1; i <= 6; i++) {
if (day & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
day = day >> 1;
}
if (end_dcf77) break;
// weekday
for (i = 1; i <= 3; i++) {
if (wday & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
wday = wday >> 1;
}
if (end_dcf77) break;
// month
for (i = 1; i <= 5; i++) {
if (month & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
month = month >> 1;
}
if (end_dcf77) break;
// year
for (i = 1; i <= 8; i++) {
if (year & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
year = year >> 1;
}
if (end_dcf77) break;
// date_parity
if (parity_date) { sendone(); } else { sendzero(); }
// wait a second
if (end_dcf77) break;
for (i = 0; i < 10; i++) { rt_task_wait_period(); if (end_dcf77) break; }
// increment minute
now_min++;
if (now_min > 59) {
now_min = 0;
now_hour++;
if (now_hour > 23) {
now_hour = 0;
now_day++;
// etc...
}
}
min = Convert2PacketBCD(now_min);
hour = Convert2PacketBCD(now_hour);
day = Convert2PacketBCD(now_day);
wday = Convert2PacketBCD(now_wday);
month = Convert2PacketBCD(now_month);
year = Convert2PacketBCD(now_year);
}
stop_rt_timer();
rt_make_soft_real_time();
rt_task_delete(handler);
return 0;
}
void clean_exit(int dummy) {
end = 1;
end_dcf77 = 1;
}
int main(void)
{
RT_TASK *maint;
int dcf77thread;
signal(SIGTERM, clean_exit);
signal(SIGINT, clean_exit);
if (!(maint = rt_task_init(nam2num("MAIN"), 1, 0, 0))) {
printf("CANNOT INIT MAIN TASK > MAIN <\n");
exit(1);
}
// create semaphore to notify main() when interrupt occurs
if (!(dspsem = rt_sem_init(nam2num("DSPSEM"), 0))) {
printf("CANNOT INIT SEMAPHORE > DSPSEM <\n");
exit(1);
}
// ask for permission to access the parallel port from user-space
if (iopl(3)) {
printf("iopl err\n");
rt_task_delete(maint);
rt_sem_delete(dspsem);
exit(1);
}
outb_p(0x10, BASEPORT + 2); //set port to interrupt mode; pins are output
// interrupt mode is not used here
// create thread
dcf77thread = rt_thread_create(dcf77_generator, NULL, 10000);
while (end_dcf77) { // wait until thread went to hard real time
usleep(100000);
}
while (!end) {
rt_sem_wait(dspsem);
printf("New minute starts now [use CTRL-C to end]\n");
}
printf("WAIT FOR COMPLETION\n");
rt_thread_join(dcf77thread);
rt_sem_delete(dspsem);
rt_task_delete(maint);
printf("DCF77SIM HAS ENDED\n");
return 0;
}
Makefile
prefix := $(shell rtai-config --prefix)
ifeq ($(prefix),)
$(error Please add <rtai-install>/bin to your PATH variable)
endif
CC = $(shell rtai-config --cc)
LXRT_CFLAGS = $(shell rtai-config --lxrt-cflags)
LXRT_LDFLAGS = $(shell rtai-config --lxrt-ldflags)
all: dcf77
dcf77: dcf77.c
$(CC) $(LXRT_CFLAGS) -o $@ $< $(LXRT_LDFLAGS)
clean:
rm -f *.o dcf77
.PHONY: clean
run.sh
#!/bin/sh
TMP=$PWD
cd /usr/realtime/modules/
insmod ./rtai_hal.ko
insmod ./rtai_lxrt.ko
insmod ./rtai_sem.ko
cd $TMP
./dcf77
rmmod rtai_sem
rmmod rtai_lxrt
rmmod rtai_hal
Non-RTAI DCF77 Simulator
Compile with:
gcc -o dcf77_soft dcf77_soft.c
dcf77_soft.c
/* DCF77 Simulator - Non-Realtime-version
A DCF77 time signal is produced on the parallel port data pins
Data on pins 2-9 (D0-D7)
GND on pins 18-25
Very handy for testing embedded hardware which needs DCF77
(C) 2005 www.captain.at */
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/io.h>
#define BASEPORT 0x378
static volatile int end_dcf77 = 1;
void sendone(void) {
int t;
outb_p(255, BASEPORT);
usleep(200000); // 1 = 200ms
outb_p(0, BASEPORT);
for (t = 1; t <= 8; t++) { usleep(100000); }
}
void sendzero(void) {
int t;
outb_p(255, BASEPORT);
usleep(100000); // 0 = 100ms
outb_p(0, BASEPORT);
for (t = 1; t <= 9; t++) { usleep(100000); }
}
unsigned int Convert2PacketBCD(unsigned int value) {
unsigned int result;
result = value % 10;
result += (value - result) / 10 * 0x10;
return result;
}
void dcf77_generator(void) {
int i, min, hour, day, wday, month, year;
int now_min, now_hour, now_day, now_wday, now_month, now_year;
time_t now;
struct tm *now_tm;
unsigned int parity_min, parity_hour, parity_date;
// Get the current date/time
now = time (NULL);
now_tm = localtime (&now);
now_min = now_tm->tm_min;
now_hour = now_tm->tm_hour;
now_day = now_tm->tm_mday;
now_wday = now_tm->tm_wday;
now_month = now_tm->tm_mon + 1;
now_year = now_tm->tm_year - 100;
min = Convert2PacketBCD(now_min);
hour = Convert2PacketBCD(now_hour);
day = Convert2PacketBCD(now_day);
wday = Convert2PacketBCD(now_wday);
month = Convert2PacketBCD(now_month);
year = Convert2PacketBCD(now_year);
end_dcf77 = 0;
while ( !end_dcf77 ) {
printf("New minute starts now [use CTRL-C to end]\n");
outb_p(0, BASEPORT);
// generate 20 zeros
for (i = 1; i <= 20; i++ ) {
sendzero();
if (end_dcf77) break;
}
// send one start bit
sendone();
// minute
parity_min = 0;
for (i = 1; i <= 7; i++) {
if (min & 1) {
sendone();
parity_min = parity_min ^ 1;
} else sendzero();
min = min >> 1;
}
if (end_dcf77) break;
// minute_parity
if (parity_min) { sendone(); } else { sendzero(); }
// hour
parity_hour = 0;
for (i = 1; i <= 6; i++) {
if (hour & 1) {
sendone();
parity_hour = parity_hour ^ 1;
} else sendzero();
hour = hour >> 1;
}
// hour_parity
if (parity_hour) { sendone(); } else { sendzero(); }
if (end_dcf77) break;
parity_date = 0;
// day
for (i = 1; i <= 6; i++) {
if (day & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
day = day >> 1;
}
if (end_dcf77) break;
// weekday
for (i = 1; i <= 3; i++) {
if (wday & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
wday = wday >> 1;
}
if (end_dcf77) break;
// month
for (i = 1; i <= 5; i++) {
if (month & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
month = month >> 1;
}
if (end_dcf77) break;
// year
for (i = 1; i <= 8; i++) {
if (year & 1) {
sendone();
parity_date = parity_date ^ 1;
} else sendzero();
year = year >> 1;
}
if (end_dcf77) break;
// date_parity
if (parity_date) { sendone(); } else { sendzero(); }
// wait a second
if (end_dcf77) break;
for (i = 0; i < 10; i++) { usleep(100000); if (end_dcf77) break; }
// increment minute
now_min++;
if (now_min > 59) {
now_min = 0;
now_hour++;
if (now_hour > 23) {
now_hour = 0;
now_day++;
// etc...
}
}
min = Convert2PacketBCD(now_min);
hour = Convert2PacketBCD(now_hour);
day = Convert2PacketBCD(now_day);
wday = Convert2PacketBCD(now_wday);
month = Convert2PacketBCD(now_month);
year = Convert2PacketBCD(now_year);
}
}
void clean_exit(int dummy) {
end_dcf77 = 1;
printf("WAIT FOR COMPLETION\n");
}
int main(void) {
signal(SIGTERM, clean_exit);
signal(SIGINT, clean_exit);
// ask for permission to access the parallel port from user-space
if (iopl(3)) {
printf("iopl err\n");
exit(1);
}
outb_p(0x10, BASEPORT + 2); //set port to interrupt mode; pins are output
// interrupt mode is not used here
dcf77_generator();
printf("DCF77SIM HAS ENDED\n");
return 0;
}
Last-Modified: Sat, 04 Feb 2006 15:43:16 GMT