User Tools

Site Tools


user:mtaft4:portfolio:hexclock

Hexadecimal Clock

Author: Matthew Taft

Objectives

  1. To create a graphical clock utilizing Hexadecimal time.
  2. To gain a better understanding of the tools and methods of manipulating data available to us.

Background

Hexadecimal time as it is known today was proposed by Mark Vincent Rogers of Intuitor and implemented as the Hexclock. This time standard divides one day into a unit called a unity, and any time of the day can be represented as a fraction of that unity in hexadecimal. Where noon would be .8000 and one hex second before it being .7FFF. The second before midnight would be represented as .FFFF with the next second starting a new day at .0000

Project

Stage 1 - Bash Code

The first and most flexible step would be to write a bash script to grab the time from the system and perform some magic on it to get it to look right. The main key here is to remember that the easiest way to get hex time is to represent the current fraction of a day in hexadecimal.

hexclock.sh
#!/bin/bash
# hexclock - convert time to a hexadecimal format
hour=`date +%H`                                         # Get the current Hour (24 hour format)
minute=`date +%M`                                       # Get the current Minute
second=`date +%S`                                       # Get the current Second
totalsec=`echo "($hour*60+$minute)*60+$second" | bc`    # Convert Hour, Minute, Second to total seconds
htime="`echo "obase=16; $totalsec/86400" | bc -l`"      # Output the hextime as a hex fraction
htime=`echo $htime | cut -d'.' -f2 | cut --bytes=1-4`   # Format the fraction to not have . and only have first 4 bytes
hexhour=`echo $htime | cut --bytes=1`                   # Hex Hours = First byte of fraction
hexmin=`echo $htime | cut --bytes=2-3`                  # Hex Minutes = Second and Third bytes of fraction
hexsec=`echo $htime | cut --bytes=4`                    # Hex Seconds = Fourth byte of fraction
echo -n "The current time in hexadecimal is: "          # Nice little formatting message
echo $hexhour"_"$hexmin"_"$hexsec                       # Print results
exit

As this example code shows it's very easy to represent this concept in the abstract nature of a bash script. Simply grab the time and convert it all to seconds, pipe a string representing the fraction to bc and tell it to output the result in base 16. Then from there it's just some string manipulation in order to cut out the bits that we want and shove them in the proper places.

Stage 2 - C++ Code

The next stage takes the general idea that we sculpted with the bash script, and moves it away from the abstraction that you can achieve by interacting with the shell. This version is still going to utilize the same sort of thing as it's going to be a command line utility still. So the main objective of this step will be just to get the time, perform the calculations that you need to in order to convert it, then display the results to the screen.

hexclock.cpp
#include <ctime>
#include <cstdio>
#include <iostream>
 
int main() {
 
	using namespace std;                                                //
	int hours, minutes, seconds, hhours, hmin1, hmin2, hseconds, htime; // Variable and Namespace setup
	int hminutes;                                                       //
	double totalsec, hexsec;
	time_t epoch_time;                                                  // Time is a time_t format
	struct tm *tm_p;
 
	epoch_time = time( NULL );                              //
	tm_p = localtime( &epoch_time );                        //   Time is extracted from Epoch time             
 
	hours = tm_p->tm_hour;                                  //
	minutes = tm_p->tm_min;                                 // Quick variable declaration to keep from using the pointers
	seconds = tm_p->tm_sec;                                 // 
	totalsec = ( ( ( hours * 60 ) + minutes ) * 60 ) + seconds; // Conversion of time to total seconds
 
 
	hexsec = totalsec / 86400.00 * 65536;  //
	htime = static_cast<int>(hexsec);      //
	hhours = htime / 4096;                 //  Block of mystical mathematics
	htime = htime - (hhours * 4096);       //  to properly format the string
	hminutes = htime / 16;                 //  so it can be displayed
	hmin1 = hminutes / 16;                 //
	hmin2 = hminutes - (hmin1 * 16);       //
	hseconds = htime - (hminutes * 16);    //
 
	printf("The time in hexadecimal is: %x_%x%x_%x\n", hhours, hmin1, hmin2, hseconds); // Quick print to screen showing the current hexadecimal time
 
	return 0;
}

Comments

As you can see the C++ code is a massive jump in size from the previous bash script. And yet it seems to do precisely the same thing. This is because it took much more doing in order to get and format the time the way I wanted in order to even come remotely close to being able to convert it. Then I had to convert each piece so that I could actually format the text the right way in order to just make a command line clock.

Now that I have at least a code base to start from I can work on throwing it into SDL and getting it to either actually display the time in a window or a widget or something or get it to determine the position of the hands of a true clock face and do it that way. The math at least for it isn't that hard, divide the 360 degree circle into 16 and 256 parts for hours and minutes respectively and have it move that percentage of the degrees each time a minute or hour changes. With how close hex seconds are to regular seconds (about 3 hex seconds to 4 regular seconds is the ratio) and the fact that there are only 16 hex seconds in a minute it would be impractical to include seconds in a clock face example but digital clock would easily be able to include seconds.

I ended up choosing C++ because that from all my fiddling with SDL was what I needed to use in order to make it work. I don't know if that is truly the case or if it's merely the fact that I never saw any strictly C SDL tutorials. Either way I did make the decision to use the cstdio library along with the standard iostream library just in case I ended up needing to use either even though I'm more comfortable with C as far is output goes which is the only thing I really foresee needing any sort of I/O for as almost all of the interaction comes between the program and the system in trying to determine the time. The only I/O is at the end when the time is reported to the user, but planning for eventualities never hurt anyone.

Stage 3: Java GUI Clock

hDigitalClock.java
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
 
public class hDigitalClock extends JPanel implements Runnable {
	int size = 7;
	int totSecs;
	long lastTime = 0, thisTime;
	digitalNumber h1, m1, m2, s1;
	GregorianCalendar cal;
	Thread th;
 
	public hDigitalClock() {
		h1 = new digitalNumber(50, 100, size);
		m1 = new digitalNumber(150, 100, size);
		m2 = new digitalNumber(230, 100, size);
		s1 = new digitalNumber(330, 100, size);
 
		setBackground(Color.WHITE);
		setLayout(new BorderLayout());
		start();
	}
 
	public void start() {
		if (th == null) {
			th = new Thread(this);
			th.start();
		}
	}
 
	public void run() {
		while (th != null) {
			try {
				totSecs = setSecs();
				showTime();
				repaint();
				Thread.sleep(0, 439453133);
			} catch (Exception e) {
			}
		}
	}
 
	public void stop() {
		if (th != null)
			th = null;
	}
 
	public int setSecs() {
		cal = new GregorianCalendar();
		int h, m, s;		
		h = cal.get(Calendar.HOUR) * 3600;
		if (cal.get(Calendar.AM_PM) == Calendar.PM)
			h += 3600 * 12;
		m = cal.get(Calendar.MINUTE) * 60;
		s = cal.get(Calendar.SECOND);
		return h+m+s;
	}
 
	public int divide(int a, int b) {
			int z = 0;
        	int i = a;
 
        	while (i >= b) {
            		i = i - b;
            		z++;
          	}
        	return z;
    }
 
	public void showTime() {
		int secs, hours, minute1, minute2;
		if(totSecs > 86399)
			totSecs=0;		
 
		secs = (int)(totSecs / 1.3183594);
		hours = divide(secs, 4096);
		secs = secs - (hours * 4096);
		minute1 = divide(secs, 256);
		secs = secs - (minute1 * 256);
		minute2 = divide(secs, 16);
		secs = secs - (minute2 * 16);
//Convert and store each place of the time to its proper value
		h1.setNumber(hours);
		m1.setNumber(minute1);
		m2.setNumber(minute2);
		s1.setNumber(secs);
	}
 
	public void showDots(Graphics2D g2) {
		g2.setColor(Color.RED);
		g2.fill(new Rectangle2D.Double(128, 65, 14, 14));
		g2.fill(new Rectangle2D.Double(128, 135, 14, 14));
		g2.fill(new Rectangle2D.Double(308, 65, 14, 14));
		g2.fill(new Rectangle2D.Double(308, 135, 14, 14));
//Create the dots between the hours and the minutes, and between the
//minutes and the seconds	
	}
 
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2 = (Graphics2D)g;
		h1.drawNumber(g2);
		m1.drawNumber(g2);
		m2.drawNumber(g2);
		s1.drawNumber(g2);
		showDots(g2);
	}
 
	public static void main(String[] a) {
//Creates our window and places the objects in it accordingly
		JFrame f = new JFrame("Digital Clock");
    	f.setSize(450, 220);
    	f.setTitle("Hexadecimal Clock");
    	f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	f.setVisible(true); 
    	f.getContentPane().add(new hDigitalClock());
	}
}
 
class digitalNumber {
	int x, y;
	int k;
	led[] leds;
 
	public digitalNumber(int x, int y, int k) {
//Draws the LEDs uniformly in their proper positions on the display		
		this.x = x;
		this.y = y;
		this.k = k;
		leds = new led[7];
		leds[0] = new led(x, y, "vert");
		leds[1] = new led(x, y+10*k, "vert");
		leds[2] = new led(x+8*k, y, "vert");
		leds[3] = new led(x+8*k, y+10*k, "vert");
		leds[4] = new led(x+2*k, y-9*k, "horiz");
		leds[5] = new led(x+2*k, y+k, "horiz");
		leds[6] = new led(x+2*k, y+11*k, "horiz");
	}
 
	public void setNumber(int num) {
//Set each LED of the display to its hex number 0-F
//each if branch handles one possibility of the state
//of the LED	
		if(num == 0) {
			leds[0].setState(true);		//top left LED
			leds[1].setState(true); 	//bottom left LED
			leds[2].setState(true); 	//top right LED
			leds[3].setState(true); 	//bottom right LED
			leds[4].setState(true); 	//top center LED
			leds[5].setState(false);	//middle center LED
			leds[6].setState(true); 	//bottom center LED
		} else if(num == 1) {
			leds[0].setState(false);
			leds[1].setState(false);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(false);
			leds[5].setState(false);
			leds[6].setState(false);			
		} else if(num == 2) {
			leds[0].setState(false);
			leds[1].setState(true);
			leds[2].setState(true);
			leds[3].setState(false);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(true);			
		} else if(num == 3) {
			leds[0].setState(false);
			leds[1].setState(false);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(true);			
		} else if(num == 4) {
			leds[0].setState(true);
			leds[1].setState(false);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(false);
			leds[5].setState(true);
			leds[6].setState(false);			
		} else if(num == 5) {
			leds[0].setState(true);
			leds[1].setState(false);			
			leds[2].setState(false);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(true);			
		} else if(num == 6) {
			leds[0].setState(true);
			leds[1].setState(true);			
			leds[2].setState(false);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(true);			
		} else if(num == 7) {
			leds[0].setState(false);
			leds[1].setState(false);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(false);
			leds[6].setState(false);		
		} else if(num == 8) {
			leds[0].setState(true);
			leds[1].setState(true);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(true);		
		} else if(num == 9) {
			leds[0].setState(true);
			leds[1].setState(false);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(true);		
		} else if(num == 10) {
			leds[0].setState(true);
			leds[1].setState(true);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(true);
			leds[5].setState(true);
			leds[6].setState(false);
		} else if(num == 11) {
			leds[0].setState(true);
			leds[1].setState(true);
			leds[2].setState(false);
			leds[3].setState(true);
			leds[4].setState(false);
			leds[5].setState(true);
			leds[6].setState(true);
		} else if(num == 12) {
			leds[0].setState(true);
			leds[1].setState(true);
			leds[2].setState(false);
			leds[3].setState(false);
			leds[4].setState(true); 
			leds[5].setState(false);
			leds[6].setState(true);
		} else if(num == 13) {
			leds[0].setState(false);
			leds[1].setState(true);
			leds[2].setState(true);
			leds[3].setState(true);
			leds[4].setState(false);
			leds[5].setState(true);
			leds[6].setState(true);
		} else if(num == 14) {
			leds[0].setState(true);
			leds[1].setState(true);
			leds[2].setState(false);
			leds[3].setState(false);
			leds[4].setState(true); 
			leds[5].setState(true);
			leds[6].setState(true);
		} else if(num == 15) {
			leds[0].setState(true);
			leds[1].setState(true);
			leds[2].setState(false);
			leds[3].setState(false);
			leds[4].setState(true); 
			leds[5].setState(true);
			leds[6].setState(false);
		}
	}
 
	public void drawNumber(Graphics2D g2) {
		for(int i=0; i<7; i++) {
			leds[i].render(g2);
		}
	}
 
	class led {
		int x, y;
		Polygon p;
		String type;
 
		public led(int x, int y, String type) {
			this.x = x;
			this.y = y;
			this.type = type;
 
			p = new Polygon();
 
			if(type == "vert") {
				p.addPoint(x, y);
				p.addPoint(x+k, y+k);
				p.addPoint(x+2*k, y);
				p.addPoint(x+2*k, y-8*k);
				p.addPoint(x+k, y-9*k);
				p.addPoint(x, y-8*k);
			}
 
			if(type == "horiz") {
				p.addPoint(x, y);
				p.addPoint(x+k, y+k);
				p.addPoint(x+5*k, y+k);
				p.addPoint(x+6*k, y);
				p.addPoint(x+5*k, y-k);
				p.addPoint(x+k, y-k);				
			}
		}
 
		public void render(Graphics2D g2){
//fills the polygons for the LEDs with the color specified in the 
//setColor method
			g2.setColor(new Color.RED);
			g2.fillPolygon(p);
		}		
}

Comments

I ended up going the path of least resistance with this implementation of the hexclock as a digital clock based in java. This is a large step away from my previous plan of using SDL to render an actual clock face and doing the math to draw the hands and such.

This method is simpler and with the code I used as a stepping point of how to actually get the numbers displayed perhaps a little more satisfying to see. All of the code to manipulate the actual traditional time into a format where it can be converted into its hex equivalent was done by me. The only things that were utilized outside of that was another source's digital clock code for actually rendering the numbers in the proper fashion.

References

user/mtaft4/portfolio/hexclock.txt · Last modified: 2011/05/17 20:09 by mtaft4