/*
------ FOXHUNT TIMER / Montreal fox controller / Type 4.1 -----

Auteur: VE2EMM Date: Jan 2003

Modified by: PE5EDW for personal customizing Date: Sep 2015
1) steady key and tone after hiders callsign until end;
2) hardcoded PE5EDW in EEPROM, any callsign can be set on request;
3) translated and added info lines in English;
4) steady LED first sec during delay time on selected fox minute;
5) no intermittent LED during delay time;
6) MO has only call_id and no TX OFF between minutes;
7) normal cw speed 8 WPM (IARU ARDF rules Part B, rev.1);
8) interval set to 5 sec to compensate for 8 WPM;
9) delay time 0/0.5/1/1.5/2/3/4/6 hrs. selectable (dip sw. 0..7);
10) multiple hiders callsigns in remarks;
11) added 30 sec. cycle when RA3 is low else 60 sec. cycle;
12) only call_id's when 2,5 min. mode is selected, no steady key;
13) in 2,5 min. cycle MOE starts with hider's callsign;
14) Changed to PIC16F628A.
15) No more using eeprom for hider's callsign, but store as constant
16) When DELAY is 0, then FOX will start immediately in its own minute

------------------ Project details -------------------

PIC16F628A Xtal = 4.194304 mHz

Port A
RA0 = LED & TONE (output)
RA1 = OOK CW (output)
RA2 = TX ON (output)
RA3 = BP select cycle_time (input)
RA4 = S1 DIP9 (input)

Port B
RB0 = DIP1
RB1 = DIP2
RB2 = DIP3
RB3 = DIP4
RB4 = DIP5
RB5 = DIP6
RB6 = DIP7
RB7 = DIP8

-------------------------------------

DIT = 1 period
DAH = 3 periods
SPACE between character = 1 periods
SPACE between letters = 3 periods
SPACE between words = 7 periods

----------------------------------------------------------------
*/

#include <16F628A.h>

#fuses xt,nowdt,noprotect,put,NOCPD,NOLVP,MCLR,BROWNOUT
#use delay (clock=4194304)
#use fast_io(A)
#use fast_io(B)


#define son pin_A0 //sound output
#define led pin_A0 //LED output
#define ook_cw pin_A1 //80 meter keying
#define tx_on pin_A2 //tx on
#define bp_jp pin_A3 //select cycle_time

//----------------- storage hiders callsign for reference --------------
// ROM is not used, only for storage of callsigns
#rom 0x2100={0x06,0x0a,0x09,0x14,0xff} //load EEDATA with ARDF

//----------------- declaration of variables ------------------

int1 flag_sec=0;
int1 flag_debut_min=0;// first minute in cycle flag
int1 flag_led=0;// LED status flag
int1 flag_ook_cw=0;// CW status flag

int8 i=0;// temporary memories
int8 j=0;// ---
int8 k=0;// ---
int8 cmpt_int=16;
int8 secondes=0;// seconds counter (max. 60)
int8 min_unit=0;// 0-10 minutes counter
int8 min_diz=0;// remaining delay time
int8 lettre_cw=0;
int8 vitesse_cw=0;// speed of CW in WPM

int8 temps_att_diz=0; // waiting time in 10 minutes
int8 timing_select=0; // 0=30 sec. & 30=1 minute cycle
int8 cycle_time=0; // 30 for 1 min. cycle.
int8 multi=1; // multiplier for time delay x2 if 30 sec. cycle.
int8 select_delay=0; // select temps_att_diz
int8 call_id=0;// ID of fox
int8 nomb_fox=0;// number of foxes in the foxhunt
int8 min_cycle=1;// number of the minute in the cycle
int8 tmpw;// memory to store W
int8 tmps;// memory to store Status
int8 hiders_digits=4;// number of letters in Hider's callsign

int16 num_periods=0;

//--------------------- Constants ---------

int8 const id[24]={0x07,0x0f,0x00,0x07,0x0f,0x02,0x07,0x0f,0x04,
0x07,0x0f,0x08,0x07,0x0f,0x10,0x07,0x0f,0x20,0x07,0x0f,0x05,
0x07,0x0f,0x09}; // MO0, MOE, MOI, MOS, MOH, MO5, MON, MOD

int8 const de[2]={0x09,0x02}; // DE

// Preset Hider's Callsign -> set also hiders_digits to correct value !!
int8 const hider[4]={0x06,0x0a,0x09,0x14}; // ARDF
//
// use the MORSE ENCODING table below to add new callsigns
// SET ALSO hiders_digits to number of digits in the callsign!!!

//----------------- Declaration of functions ------------------

void pic_ini(void);// initiate PIC ports
void lire_dip(void);// read dip switches
void chasse_ini(void);// initiate foxhunt settings
void temps_attente(void);// countdown delay
void foxhunt(void);// start foxhunt

void chronometrage(void);// interrupt driven clock
void timing(void);// cycle timer routine

void une_lettre(char lettre_cw);// CW code
void tone(int8 nom_dit);// generate tones
void no_tone(int8 nom_dit);// generate silence

//****************************** Main program ********************

main()
{
pic_ini();
lire_dip(); // read dip switch
chasse_ini(); // set foxhunt parameters
temps_attente(); // wait for startup time
foxhunt(); // foxhunt
}

//************************* end of Main program *******************

void pic_ini(void)//----------------------------
{
output_A(0x00);// clear ports A
output_B(0x00);// clear ports B
set_tris_A(0b00011000);// set A 0-2 as outputs, A 3-4 as inputs
set_tris_B(0b11111111);// set all B ports as outputs
port_b_pullups(true);// set B ports internal pull-up
}

void lire_dip(void)//------------------- read the dip switches --------
{
timing_select=input(bp_jp);
if(timing_select==1)
{
multi=1;
cycle_time=30;
}
else
{
multi=2;
}
k=input_b();
// waiting time in 10 minutes before the foxhunt
// 8 menu options available
select_delay=(k&0b00000111);
switch (select_delay) {
case 0: temps_att_diz=0 ; // no delay
break;
case 1: temps_att_diz=(3*multi) ; // 30 min. delay
break;
case 2: temps_att_diz=(6*multi) ; // 1 hr. delay
break;
case 3: temps_att_diz=(9*multi) ; // 1 hrs. 30 min. delay
break;
case 4: temps_att_diz=(12*multi) ; // 2 hrs. delay
break;
case 5: temps_att_diz=(15*multi) ; // 2 hrs. 30 min. delay
break;
case 6: temps_att_diz=(18*multi) ; // 3 hrs. delay
break;
case 7: temps_att_diz=(21*multi) ; // 3 hrs. 30 min. delay
break;
default: temps_att_diz=0 ; // default value is no delay
break; }
// --- call id of the fox
call_id=(k&0b00111000);
shift_right(&call_id,1,0);
shift_right(&call_id,1,0);
shift_right(&call_id,1,0);
// number of foxes in the foxhunt
shift_left(&nomb_fox,1,input(pin_a4));
shift_left(&nomb_fox,1,input(pin_b7));
shift_left(&nomb_fox,1,input(pin_b6));
// fault mode, id of the fox > number of foxes
while(call_id>nomb_fox)
{
output_high(led);// fast intermittent LED
delay_ms(127);
output_low(led);
delay_ms(127);
}
}

void chasse_ini(void)//-------- initiate foxhunt parameters --------
{
cmpt_int=16;
min_diz=temps_att_diz;
secondes=0;
// when delay=0 and not MO then instant start
if((temps_att_diz==0)&&(call_id!=0))
{
min_unit=(call_id-1); // set counter to first active minute
min_cycle=call_id; // set active minute cylce to call_id
}
else
{
min_unit=0; // normal setting
min_cycle=1; // normal setting
}
set_rtcc(0);
setup_counters(rtcc_internal,rtcc_div_256);
enable_interrupts(INT_RTCC);
enable_interrupts(global);
}

void temps_attente(void)//-------- waiting for the foxhunt------
{

if(min_diz>0)// check for remaining delay time
{
do
{
timing();

if((min_cycle==call_id)&&secondes==0)// LED blinks first sec on active
{
output_high(led);
}
else
{
output_low(led);
}
}
while(min_diz>0);// loop while delay time remaining
}
flag_debut_min=1;
}

void foxhunt(void)//-------------- foxhunt (this is it!!)---------------
{
flag_ook_cw=1;
while(true)
{
timing();
while(((min_cycle==call_id)||(call_id==0))&&flag_debut_min==1)
{
output_high(tx_on); //0 sec
flag_debut_min=0;
vitesse_cw=8;
if((timing_select==0)&&(call_id==1)) // MOE only in 30 sec. mode
{
while(secondes<1); // 1 sec
vitesse_cw=20; // set cw speed to high
for(i=0;i<=1;i++)une_lettre(de[i]);//sent DE
no_tone(7);//space
i=0;
//sent Hider's Callsign
for(i=0;i<=(hiders_digits-1);i++)une_lettre(hider[i]);
i=0;
vitesse_cw=8; // set cw speed to normal
}
else // normal operation in 60 sec. mode
{
while(secondes<1); // 1 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
}
while(secondes<6); // 6 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<11); // 11 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<16); // 16 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<21); // 21 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<26); // 26 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
if(timing_select==1) // only in 60 sec. mode
{
while(secondes<31); // 31 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<36); // 36 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<41); // 41 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<46); // 46 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
if(call_id!=0) // not MO -> do callsign and tone
{
while(secondes<51); // 51 sec
vitesse_cw=20; // set cw speed to high
for(i=0;i<=1;i++)une_lettre(de[i]);//sent DE
no_tone(7);//space
i=0;
//sent Hider's Callsign
for(i=0;i<=(hiders_digits-1);i++)une_lettre(hider[i]);
i=0;
while(secondes<56); // 56 sec
vitesse_cw=8; // set cw speed to normal
tone(28); // key/tone on until end
while(secondes<59); // 59 sec
output_low(tx_on); // stop tx
}
else // MO does only call_id and TX stays ON
{
while(secondes<51); // 51 sec
for(i=0;i<=2;i++)une_lettre(id[(call_id*3)+i]);
while(secondes<56); // 56 sec
vitesse_cw=20; // set cw speed to high
for(i=0;i<=1;i++)une_lettre(de[i]);//sent DE
no_tone(7);//space
i=0;
//sent Hider's Callsign
for(i=0;i<=(hiders_digits-1);i++)une_lettre(hider[i]);
i=0;
vitesse_cw=8; // set cw speed to normal
}
}
else // only in 30 sec. mode
{
while(secondes<29); // 29 sec
output_low(tx_on); // stop tx
}
}
}
}

//***********************************************************************

#int_global//-------- interrupt driven clock function ------------------
void chronometrage(void)
{
#asm // -------------------------- ** PUSH **
MOVWF TMPW // SAUVER W DANS TMPW
SWAPF 0x03,W // Charger le STATUS dans W
MOVWF TMPS // Sauver le STATUS dans TMPS
#endasm
if(--cmpt_int==0)
{
cmpt_int=16;// 16
++secondes;// max secondes=60;
flag_sec=1;
}
//---------------------------- ** POP **
#asm
BCF 0x0B,2 // Rappel du flag du zero de TMRO
SWAPF TMPS,W // replacer W, STATUS
MOVWF 0x03
SWAPF TMPW,F // replacer STATUS
SWAPF TMPW,W
RETFIE // Return to the program
#endasm
}

//--------------------- other functions -----------------------

void timing(void)//- finir chronometrage a l'exterieur de l'interruption --
{
if(secondes>=(cycle_time+30))// check for 30 or 60 secs passed
{
secondes=0;
++min_unit;// increase minutes counter

++min_cycle;// increase minute in cycle counter
if(min_cycle>nomb_fox)min_cycle=1;// reset if last cycle min. has passed
flag_debut_min=1;// flag first minute in cycle
}
if(min_unit>=10)// check number of minutes passed more than 9
{
min_unit=0;// reset 0-10 minutes counter
if(min_diz>0)min_diz--;//decrease delay time
}
}

//************************ generation CW ******************

void une_lettre(char lettre_cw)// ------ send a character -----------
{
if(lettre_cw==0)no_tone(5);
while(lettre_cw>1)
{
if(shift_right(&lettre_cw,1,0))// dah
{
tone(3);
no_tone(1);
}
else // dit
{
tone(1);
no_tone(1);
}
}

no_tone(2);
}

void tone(int8 nom_dit)// ------------ dits --------------
{
num_periods = (1000/vitesse_cw)*nom_dit;// speed in wpm
if(flag_ook_cw)output_high(ook_cw);
while((num_periods--) != 0)
{
delay_us(255);
delay_us(255);
output_high(son);// positive half cycle
delay_us(255);
delay_us(255);
output_low(son);// negative half cycle
}
output_low(ook_cw);
}

void no_tone(int8 nom_dit)// ---------- spaces -------------
{
num_periods = (1000/vitesse_cw)*nom_dit;// speed in wpm
while((num_periods--) != 0)
{
delay_us(255);
delay_us(255);
delay_cycles(1);
delay_us(255);
delay_us(255);
delay_cycles(1);
}
}

//********************** end of generating CW ******************

//----------------------- end of program ----------------------

/*
=== SNIP ==================================================================

MORSE ENCODING ...

One morse character per BYTE, bitwise, LSB to MSB.

0 = dit, 1 = dah. The byte is shifted to the right bit by bit, until the
last 1 is left, this 1 is an END OF CHARACTER indicator.
A maximum of 7 elements can be encoded, (error) is excluded.

SWITCHES
CODE MSD LSD HEX Comments
------ --- --- --- --------------------
KN -.--. 00101101 2 13 2d Go only
SK ...-.- 01101000 5 8 58 Clear
AR .-.-. 00101010 2 10 2a Over, end of message
BT -...- 00110001 3 1 31 Pause
AS .-... 00100010 2 2 22 Wait, stand by
/ -..-. 00101001 2 9 29
0 ----- 00111111 3 15 3f
1 .---- 00111110 3 14 3e
2 ..--- 00111100 3 12 3c
3 ...-- 00111000 3 8 38
4 ....- 00110000 3 0 30
5 ..... 00100000 2 0 20
6 -.... 00100001 2 1 21
7 --... 00100011 2 3 23
8 ---.. 00100111 2 7 27
9 ----. 00101111 2 15 2f
A .- 00000110 0 6 06
B -... 00010001 1 1 11
C -.-. 00010101 1 5 15
D -.. 00001001 0 9 09
E . 00000010 0 2 02
F ..-. 00010100 1 4 14
G --. 00001011 0 11 0b
H .... 00010000 1 0 10
I .. 00000100 0 4 04
J .--- 00011110 1 14 1e
K -.- 00001101 0 13 0d
L .-.. 00010010 1 2 12
M -- 00000111 0 7 07
N -. 00000101 0 5 05
O --- 00001111 0 15 0f
P .--. 00010110 1 6 16
Q --.- 00011011 1 11 1b
R .-. 00001010 0 10 0a
S ... 00001000 0 8 08
T - 00000011 0 3 03
U ..- 00001100 0 12 0c
V ...- 00011000 1 8 18
W .-- 00001110 0 14 0e
X -..- 00011001 1 9 19
Y -.-- 00011101 1 13 1d
Z --.. 00010011 1 3 13
SPACE 00000000 0 0 00 Word space (special exception)
EOM 11111111 15 15 ff End of message (special exception)**

=== SNIP ==================================================================
*/