/* CW monitor uses RI signal on COM1 (pin 8 of DB25 pin 9 on a DB9) */ /* input stimulus. A space = +5v, mark = 0v works with RS 232 systems */ /* CTS and DSR must be tied to space level (5v) for port to operate */ /* CTS is 5/DB25 8/DB9 DSR is 6/DB25 6/DB9 */ #include #include #include #include #include #include #include #define SIZE 32 #define PRINT_CHAR 0 #define PRINT_SPACE 1 #define PRINT_OVR 2 #define FLUSH 1 #define UPDATE 2 #define MARK 0x80 #define SPACE 0 #define RESET 0 #define SET 1 #define ON 1 #define OFF 0 #define DIT 1 #define DAH 2 #define NULL 0 #define HIGH 1 #define LOW 0 #define START 0 #define STOP 2 #define FINISH 1 #define READ 1 #define NORMAL 7 #define UP 6 #define DOWN 7 #define BELL 7 #define TICK 1 #define NOTICK 0 union REGS regs; struct WINDOW { unsigned char tlrow; unsigned char tlcol; unsigned char brrow; unsigned char brcol; unsigned char attr; }; struct WINDOW screen = {11,0,19,79,NORMAL}; struct WINDOW spadscrn = {21,0,24,79,NORMAL}; int space_msec, mark_msec, temp, old_vmode, space_flag, row, col, loop, buffer[9], *bufptr, word_flag, word_mul, char_mul, prikey, char_flag, mark_count, init_flag, spad_flag, spad_row, spad_col, mark_stack[SIZE], *mark_ptr, md_stack[SIZE], *md_ptr, fence, *temp_ptr, sp_buffer[8], *sp_ptr, sp_count, filter_co, write_flag, write_char, tempkey, e_col, e_row, key, sec_char, dit_msec, skew_count, delta_sec, delta_msec, speed, wpm_flag, teach; long avg, md_avg, md_val; /* func declarations */ void char_print(int c_flag); int read_port(int next_state); void space_func(void); void mark_func(void); int stop_watch(void); void entry_func(void); void write(char *strptr); void scroll(struct WINDOW window, int lines, int dir); void spot_print(int attr, val); void filter_print(int attr); void auto_term(void); void wpm_func(void); void char_out(void); void diagnostic(void); /* initialize program */ /* set up nominal operation parameters */ main() { teach = OFF; init_flag = SET; spad_flag = OFF; row = 11; col = 0; spad_row = 21; spad_col = 0; prikey = 0; write_flag = RESET; fence = 100; filter_co = 32; char_mul =25; word_mul = 67; skew_count = 0; for(temp_ptr = &mark_stack[0]; temp_ptr <= &mark_stack[SIZE -1]; temp_ptr++) *temp_ptr = 100; for(temp_ptr = &md_stack[0]; temp_ptr <= &md_stack[SIZE -1]; temp_ptr++) *temp_ptr = 0; mark_ptr = &mark_stack[0]; md_ptr = &md_stack[0]; sp_ptr = &sp_buffer[0]; sp_count = 0; bufptr = &buffer[0]; for(loop = 0; loop < 8; loop++) {*bufptr = NULL; bufptr++; } mark_count = 0; bufptr = &buffer[0]; /* set up com1 as 4800bd 7 bits 1 stop, 1 start, no parity */ /* fakes a real good 2 ms time clock */ regs.x.dx = 0; regs.h.ah = 0; regs.h.al = 0xC2; int86(0x14,®s,®s); /* set up entry and working screens */ /* code displayed on screen 0 */ old_vmode = getvmode(); setvmode(3); setcolor(9,3); setapage(0); clrscrn(); poscurs(0,7); write("ROBO-COPy 73 Magazine Automated CW Copier by WB9DYI/Mike Hansen"); poscurs(2,7); write("Change a copy parameter by typing it's first letter (L,W,F); then "); poscurs(3,7); write("modify value using +/- keys. Terminate change by pressing Enter key."); poscurs(4,7); write("Type H for help; Q exits to DOS; R resets copy parameters. Space bar"); poscurs(5,7); write("toggles scratch pad area on/off. Type S to clear scratch pad area,"); poscurs(6,7); write("C to clear copy area. M toggles between normal and teach modes."); poscurs(8,2); write("Letter Timing dits. Word Timing dits. Mode Filter"); poscurs(10,0); writechs('_',0x07,80); poscurs(10,33); write("WPM = "); poscurs(20,0); writechs('-',0x07,80); poscurs(20,30); write("Scratch Pad "); setvpage(0); /* copy starts at row 10 col 0 */ /* v_page 0 cleared for copy */ /* set up help screen, v_ page 1 */ setapage(1); setcolor(9,3); poscurs(0,9); write("Intended for private use only. All commercial rights reserved."); poscurs(2,27); write("ROBO-COPy HELP SCREEN"); poscurs(3,10); write("Decrease letter timing when the over run error "); poscurs(4,10); write("symbol # ocurrs frequently."); poscurs(6,10); write("Increase letter timing when longer characters"); poscurs(7,10); write("break-up into short letters; example SOS = EEETTTEEE."); poscurs(9,10); write("If wordsruntogetherlikethis...decrease word timing."); poscurs(11,10); write("If letters a r e separated in words...increase word timing."); poscurs(13,10); write("A sampling filter helps reduce the effects of QRN. It has three "); poscurs(14,10); write("settings, LOW, MEDIUM and HI. LOW filtering is best for"); poscurs(15,10); write("high speed reception, but is more susceptible to noise."); poscurs(16,10); write("When receiving noisy signals, HI filtering works to eliminate the"); poscurs(17,10); write("interference but reduces copy speed to less than 30-35 WPM."); poscurs(19,10); write("Unrecognized characters and errors are displayed as *."); poscurs(20,10); write("Command characters like are displayed in reverse video."); poscurs(20,34); writechs('S',0x70,1); poscurs(20,35); writechs('K',0x70,1); poscurs(21,10); write("Keep important QSO data like call signs, etc. in Scratch Pad"); poscurs(22,10); write("by toggling area ON/OFF with Space bar. Type S to clear the area."); poscurs(24,13); write("Press space bar for more HELP, Press Enter key to exit HELP."); /* screen 1 finished */ setapage(2); poscurs(0,23); write("Recommend receiver operation for ROBO-COPy"); poscurs(2,10); write("The ROBO-COPy interface can be connected in parallel with either"); poscurs(3,10); write("the speaker or low-impedance headphone outputs. The added load is"); poscurs(4,10); write("about 330 ohms, and should have no noticeable effect on the quality"); poscurs(5,10); write("or volume of the audio output. The output of the ROBO-COPy "); poscurs(6,10); write("interface should be connected to the COM1 port of the P.C.."); poscurs(8,30); write("ROBO-COPying a CW station"); poscurs(10,20); write("AGC = FAST, Noise Blanker = ON, CW Filter = ON"); poscurs(11,10); write("1. Tune station as you would normally do for cw reception."); poscurs(12,10); write("2. Reduce audio and RF gain to the MINIMUM needed for solid copy."); poscurs(13,10); write("3. Increase audio gain until LED on ROBO-COPy interface flashes"); poscurs(14,10); write(" bright, distinct dits and dahs."); poscurs(24,25); write("Press any key to exit HELP."); setapage(0); setvpage(0); do {if(read_port(MARK) != SPACE) mark_func(); space_func(); }while (0 == 0); }/* end of main brace */ /* start of program */ void space_func(void) { word_flag = START; char_flag = START; /* start assembling character */ space_msec = 0; while(read_port(MARK) == SPACE) { if(stop_watch() == TICK) space_msec = space_msec + 2; /* check keyboard */ key = getkey(); if(key != EOF || init_flag == SET) entry_func(); /* background task of displaying data */ if(write_flag == SET) /* write to screen */ { char_out(); if(sec_char != 0) {write_char = sec_char; char_out(); } } if(teach == OFF) { dit_msec = avg/2; if(space_msec > (char_mul * dit_msec)/10 && char_flag == START && mark_count > 0) {char_print(PRINT_CHAR); char_flag = FINISH; wpm_func(); /* outputwpm */ } if(space_msec > (word_mul * dit_msec)/10 && word_flag == START) {word_flag = FINISH; char_print(PRINT_SPACE); } } } /* done with background house keeping now back to biz */ /* process space */ if(teach == ON) { dit_msec = avg/2; if(space_msec > (char_mul * dit_msec)/10 && char_flag == START && mark_count > 0) {char_print(PRINT_CHAR); char_flag = FINISH; wpm_func(); /* outputwpm */ if(write_flag == SET) /* write to screen */ {char_out(); if(sec_char != 0) {write_char = sec_char; char_out(); } } } if(space_msec > (word_mul * dit_msec)/10 && word_flag == START) {word_flag = FINISH; char_print(PRINT_SPACE); char_out(); } } return; /* return on mark */ } /* end of space_func */ void mark_func(void) { /* MARK detected in space_func is timed here */ sp_count++; if(sp_count > 7) {sp_count = 0; sp_ptr = &sp_buffer[0]; } *sp_ptr = space_msec; sp_ptr++; mark_msec = 2; /* add 2 for initial loop-compensates for sampling time */ /* check for end of mark */ while(read_port(SPACE) != SPACE) {if(stop_watch() == TICK) {if(mark_msec < 1000) mark_msec = mark_msec + 2; } } /* is mark within range ? */ if(mark_msec < 10 ) return; mark_count++; if(mark_msec > fence) *bufptr = DAH; else *bufptr = DIT; bufptr++; if(bufptr > &buffer[8]) char_print(PRINT_OVR); /* calculate latest mean deviation and store on stack */ md_ptr++; if(md_ptr > &md_stack[SIZE -1]) md_ptr = &md_stack[0]; *md_ptr = mark_msec - avg; /* calculate new average of mean deviation */ md_avg = 0; for(temp_ptr = &md_stack[0]; temp_ptr <= &md_stack[SIZE -1]; temp_ptr++) md_avg = md_avg + *temp_ptr; md_avg = md_avg/SIZE; /* generate absolute value of mean deviation times 4 */ md_val = 4 * md_avg; if(md_val < 0) md_val = -1 * md_val; /* avoid corrupting average with skew due to repetition */ if(mark_msec < *mark_ptr + (*mark_ptr/8) || mark_msec > *mark_ptr - (*mark_ptr/8)) skew_count++; else skew_count = 0; if(skew_count < 4 || md_val > avg) { /* load mark_stack with last mark */ mark_ptr++; if(mark_ptr > &mark_stack[SIZE -1]) mark_ptr = &mark_stack[0]; *mark_ptr = mark_msec; /* calculate new average */ avg = 0; for(temp_ptr = &mark_stack[0]; temp_ptr <= &mark_stack[SIZE -1]; temp_ptr++) avg = avg + *temp_ptr; avg = avg/SIZE; /* calculate new fence point */ fence = avg + md_avg; } return; } /* end of mark_func */ void char_print(int c_flag) { int temp, exp, loop, sum; char letter[] ="ETIANMSURWDKGOHVF*L*PJBXCYZQ"; char alpha; char *letter_ptr, *number_ptr; sec_char = 0; /* reset special command character value */ write_flag = SET; if(c_flag == PRINT_OVR) write_char = '#'; if(c_flag == PRINT_SPACE) write_char = ' '; else{ bufptr = &buffer[7]; sum = 0; exp = 128; for(loop = 0; loop < 8; loop++) {temp = *bufptr; while(*bufptr == 0) {exp = exp/2; bufptr--; } } bufptr = &buffer[0]; while(exp != 0) { sum = sum + ((*bufptr) * exp); exp = exp/2; bufptr++; } if(mark_count == 0) return; if((mark_count <= 4) && (sum < 29)) {letter_ptr = &letter[0]; alpha = *(letter_ptr + (sum - 1)); write_char = alpha; } if(mark_count== 5) {switch(sum){ case 62: write_char = '0'; break; case 61: write_char = '9'; break; case 59: write_char = '8'; break; case 55: write_char = '7'; break; case 47: write_char = '6'; break; case 31: write_char = '5'; break; case 32: write_char = '4'; break; case 34: write_char = '3'; break; case 38: write_char = '2'; break; case 46: write_char = '1'; break; case 33: write_char = 'S'; sec_char = 'N'; break; case 41: write_char = 'A'; sec_char = 'R'; break; case 39: write_char = 'A'; sec_char = 'S'; break; case 52: write_char = 'K'; sec_char = 'A'; break; case 48: write_char = '='; break; case 49: write_char = '/'; break; case 53: write_char = '('; break; default: write_char = '*'; } } if(mark_count == 6) {switch(sum) {case 68: write_char = 'S'; sec_char = 'K'; break; case 75: write_char = '?'; break; case 84: write_char = '.'; break; case 114: write_char = ','; break; case 93: write_char = '''; break; case 76: write_char = 'I'; sec_char = 'Q'; break; case 83: write_char = 'A'; sec_char = 'L'; break; case 81: write_char = '"'; break; case 105: write_char = ';'; break; case 96: write_char = '-'; break; case 108: write_char = ')'; break; case 119: write_char = ':'; break; default: write_char = '*'; } } if(mark_count == 7) {if(sum == 136) write_char = '$'; else write_char = '*'; } if(mark_count == 8) {if(sum == 255) {write_char = 'H'; sec_char = 'H'; } else write_char = '*'; } }/* end of else */ for(bufptr = &buffer[0]; bufptr <= &buffer[7]; bufptr++) *bufptr = 0; /* clear DIT/DAH buffer */ bufptr = &buffer[0]; mark_count = 0; return; } /* end of char_print function */ int read_port(int next_state) {int fcount, temp, delay; /* read_port has a sampling filter built in. There must be */ /* filter_co times the next state before a transistion is reported via */ /* the returned value */ fcount = 0; do {fcount++; regs.x.dx = 0; regs.h.ah = 3; int86(0x14,®s,®s); temp = regs.h.al & 0x80; for(delay = 0; delay < 16; delay++) ; }while(temp == next_state && fcount < filter_co); if(fcount >= filter_co) temp = next_state; /* successfully completed filter */ else {if(next_state == MARK) /* noisy signal returns current state */ temp = SPACE; else temp = MARK; } return(temp); } /* stop_watch generates a 2 ms wait */ int stop_watch(void) {int loop; regs.x.dx = 0; regs.h.ah = 3; int86(0x14,®s,®s); if(regs.h.ah & 0x40 == 0) return(NOTICK); /* xmit buffer not empty */ regs.x.dx = 0; /* xmit buffer empty, fill it */ regs.h.ah = 1; regs.h.al = ' '; int86(0x14,®s,®s); return(TICK); } void write(char *strptr) {int row, col; row = cursrow(); col = curscol(); while(*strptr != NULL) {writechs(*strptr, 0x07,1); strptr++; col++; poscurs(row,col); } return; } void entry_func(void) { cursoff(); e_row = 8; /* all parameters are on row 8 */ tempkey = key & 0x00FF; /* makes sure key is upper case */ if(tempkey == '~') {diagnostic(); return; } if(tempkey > 0x60) tempkey = tempkey - 32; if(init_flag == SET) {init_flag = RESET; tempkey = 'R'; } switch(tempkey) { case 'C': scroll(screen, 0 , UP); /* clear scratch pad screen */ row = 11; col = 0; break; case 'F': if(prikey != 0) auto_term(); filter_print(0x70); prikey = tempkey; break; case 'H': setvpage(1); /* Help just changes screens */ temp = getch(); if(temp == ' ') {setvpage(2); temp = getch(); } setvpage(0); break; case 'L': if(prikey != 0) auto_term(); e_col = 16; /* highlite letter selection */ spot_print(0x70, char_mul); prikey = tempkey; break; case 'W': if(prikey != 0) auto_term(); e_col = 39; spot_print(0x70, word_mul); prikey = tempkey; break; case ' ': if(spad_flag == ON) /* toggle scratch pad */ {spad_flag = OFF; poscurs(20,42); writechs('O', 0x07,1); poscurs(20,43); writechs('F',0x07,2); } else{ spad_flag = ON; poscurs(spad_row, spad_col); writechs('|', 0x07, 1); spad_col++; if(spad_col > 79) {spad_col = 0; spad_row++; if(spad_row > 24) {spad_row = 24; scroll(spadscrn, 1,UP); } } poscurs(20,42); writechs('O',0x70,1); poscurs(20,43); writechs('N',0x70,1); poscurs(20,44); writechs(' ',0x07,1); } break; case '+': if(prikey != 0) {if(prikey == 'L') {char_mul++; if(char_mul > 45) char_mul = 45; e_col = 16; spot_print(0x70, char_mul); } if(prikey == 'W') {word_mul = word_mul + 1; if(word_mul > 95) word_mul = 95; e_col = 39; spot_print(0x70, word_mul); } if(prikey == 'F') {filter_co = filter_co + filter_co; if(filter_co > 64) filter_co = 16; filter_print(0x70); } } else putch(BELL); break; case '-': if(prikey != 0) {if(prikey == 'L') {char_mul--; if(char_mul < 5) char_mul = 5; e_col = 16; spot_print(0x70, char_mul); } if(prikey == 'W') {word_mul = word_mul - 1; if(word_mul < 25) word_mul = 25; e_col = 39; spot_print(0x70, word_mul); } if(prikey == 'F') {filter_co = filter_co/2; if(filter_co < 16) filter_co = 64; filter_print(0x70); } } else putch(BELL); break; case 'R': char_mul = 17; word_mul = 67; filter_co = 32; prikey = 0; e_col = 16; poscurs(e_row, e_col); spot_print(0x07, char_mul); e_col = 39; poscurs(e_row, e_col); spot_print(0x07, word_mul); e_col = 69; poscurs(e_row, e_col); filter_print(0x07); spad_flag = OFF; poscurs(20,42); writechs('O', 0x07,1); poscurs(20,43); writechs('F',0x07,2); poscurs(8,55); if(teach == ON) printf("Teach "); else printf("Normal"); break; case 'S': scroll(spadscrn, 0 , UP); spad_col = 0; spad_row = 21; break; case 0x0D: prikey = 0; e_col = 16; poscurs(e_row, e_col); spot_print(0x07, char_mul); e_col = 39; poscurs(e_row, e_col); spot_print(0x07, word_mul); e_col = 69; poscurs(e_row, e_col); filter_print(0x07); break; case 'Q':setvmode(old_vmode); regs.x.dx = 0; /* Restore COM1 */ regs.h.ah = 0; regs.h.al = 0xE3; /* re-set to 9600 bd 1 Start, 1 stop, no parity, 8 bits */ int86(0x14,®s,®s); curson(); exit(0); case 'M': poscurs(8,55); if(teach == ON) {teach = OFF; printf("Normal"); } else{ teach = ON; printf("Teach "); } break; default: putch(BELL); break; }/* end of switch*/ poscurs(row,col); curson(); return; /* return from entry_func */ } /* end of entry_func */ void spot_print(int attr, val) {int tens, ones; tens = val/10; ones = val - (10*tens); poscurs(e_row, e_col); if(tens == 0) tens = -0x10; /* fake a space */ writechs(tens + 0x30, attr, 1); e_col++; poscurs(e_row, e_col); writechs('.', attr, 1); e_col++; poscurs(e_row, e_col); writechs(ones + 0x30, attr, 1); return; } void filter_print(int attr) { e_col = 71; if(filter_co == 16) {poscurs(e_row, e_col); writechs('L', attr, 1); e_col++; poscurs(e_row, e_col); writechs('O', attr, 1); e_col++; poscurs(e_row, e_col); writechs('W', attr, 1); } if(filter_co == 32) {poscurs(e_row, e_col); writechs('M', attr, 1); e_col++; poscurs(e_row, e_col); writechs('E', attr, 1); e_col++; poscurs(e_row, e_col); writechs('D', attr, 1); } if(filter_co == 64) {poscurs(e_row, e_col); writechs('H', attr, 1); e_col++; poscurs(e_row, e_col); writechs('I', attr, 1); e_col++; poscurs(e_row, e_col); writechs(' ', attr, 1); } return; } /* end of filter_print*/ void scroll(struct WINDOW window, int lines, int dir) { regs.h.ah = dir; regs.h.al = lines; regs.h.ch = window.tlrow; regs.h.cl = window.tlcol; regs.h.dh = window.brrow; regs.h.dl = window.brcol; regs.h.bh = window.attr; int86(0x10,®s,®s); return; } void auto_term(void) { e_row = 8; e_col = 16; spot_print(0x07, char_mul); e_col = 39; spot_print(0x07, word_mul); filter_print(0x07); return; } void wpm_func(void) {int wpm, huns, tens; if(avg <= 0) {poscurs(10, 39); writechs('*',0x07,3); return; } wpm = 2400/avg; huns = wpm/100; wpm = wpm - (huns * 100); tens = wpm/10; wpm = wpm - (tens * 10); poscurs(10,39); if(huns == 0) huns = -0x10; writechs(huns + 0x30, 0x07, 1); poscurs(10,40); if(tens == 0 && huns == -0x10 ) tens = -0x10; /* fake a space */ writechs(tens + 0x30, 0x07,1); poscurs(10,41); writechs(wpm + 0x30, 0x07,1); return; } void char_out(void) { int attr; write_flag = RESET; attr = 0x07; if(sec_char != 0) attr = 0x70; poscurs(row, col); writechs(write_char, attr, 1); col++; if(col > 79) {col = 0; if(row < 19) row++; else scroll(screen, 1, UP); } if(spad_flag == ON) {spad_col++; if(spad_col > 79) {spad_col = 0; if(spad_row < 24) spad_row++; else scroll(spadscrn, 1, UP); } poscurs(spad_row, spad_col); writechs(write_char, attr, 1); } poscurs(row,col); return; } void diagnostic(void) {int count, *ptr; scroll(screen, 0, UP); poscurs(11,0); printf("Average mark milliseconds %li\n",avg); printf("Last 8 mark times in milliseconds\n"); ptr = mark_ptr; for(count = 0; count < 8 ; count++) {printf("%i ", *ptr); ptr--; if(ptr < &mark_stack[0]) ptr = &mark_stack[SIZE -1]; } printf("\n"); printf("Average deviation from mean milliseconds %li\n",md_avg); printf("Last 8 mean deviation times in milliseconds\n"); ptr = md_ptr; for(count = 0; count <8 ; count++) {printf("%i ", *ptr); ptr--; if(ptr < &md_stack[0]) ptr = &md_stack[SIZE - 1]; } printf("\n"); printf("Last 8 space times in milliseconds\n"); ptr = sp_ptr; for(count = 0; count < 8 ; count++) {printf("%i ", *ptr); ptr--; if(ptr < &sp_buffer[0]) ptr = &sp_buffer[7]; } printf("\n"); count = getch(); scroll(screen,0,UP); row = 11; col = 0; return; }