Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


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

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top