Home |
Search |
Today's Posts |
#1
![]() |
|||
|
|||
![]()
I'm working on a telemetry board and wish to include software FSK1200
(MX-614's are getting harder to find). The official Bell 202 spec is out of print and hard to find as well, so I pieced this together from what I can find. Multimon won't decode the output, so I'm likely doing something wrong. This is supposed to be (once it works) my reference implementation before I port it to my telemetry board, so I'm trying to keep it as simple as possible. The program outputs 22050 hz 8-bit signed data to stdout; gcc fsk.c -o fsk -lm; ./fsk test; sox -s -b -r 22050 test -w test.wav Phase is coherent, and there are peaks at 1200 and 2200 in the spectrum analysis. Any help or suggestions would be appreciated. Robert Cicconetti KG4MVB -- begin fsk.c -- #include math.h #include stdio.h // $#@$#@ //#define BAUDRATE 1200 // octet rate is 150 octet / sec. (1200 / 8). BIT period is // .000666, or 1/1500 (10 bits per symbol, including start and stop). // BAUDRATE is therefor used improperly here; fixme. #define BAUDRATE 1500 #define MARK 1200 #define SPACE 2200 // For convenience when working with multimon #define SAMPLERATE 22050 double sendbyte(char a, double angle); int main (void) { // 8 bit output; encoder DAC is likely to be only 3 or 4 bit short outbyte; int i; double angle = 0; char test[]= "fm KG4MVB-0 to FFFFFF-0 SABM+"; for (i = 0 ; i 5000; i++) putchar(0); //PREAMBLE for (i = 0 ; i 24; i++) sendbyte(0, angle); for (i = 0 ; i 29 ; i++) { angle = sendbyte(test[i], angle); } for (i = 0 ; i 2; i++) sendbyte(0, angle); for (i = 0 ; i 50000; i++) putchar(0); return 0; } double sendbyte(char a, double angle) { // LSB first. Start bit MARK end bit SPACE // MARK is 1, SPACE is 0 // double phase_offset = 0; int freq, j = 0, i; char bit; for (i = 0; i 10; i++) { if (i == 0) bit = 1; else if (i == 9) bit = 0; else bit =( a (i-1)) & 0x1; if (bit == 1) freq = MARK; else freq = SPACE; phase_offset = angle - (double)2*M_PI*freq*((double)(j-1)/SAMPLERATE); while (j ((i+1)*SAMPLERATE)/BAUDRATE ) { angle = (double)2*M_PI*freq*((double)j/SAMPLERATE) + phase_offset; putchar((short)(0.5*sin(angle)*128)); j++; } } return angle; } |
#2
![]() |
|||
|
|||
![]()
Dana Myers K6JQ wrote in message ws.com...
R C wrote: I'm working on a telemetry board and wish to include software FSK1200 (MX-614's are getting harder to find). The official Bell 202 spec is out of print and hard to find as well, so I pieced this together from what I can find. Multimon won't decode the output, so I'm likely doing something wrong. This is supposed to be (once it works) my reference implementation before I port it to my telemetry board, so I'm trying to keep it as simple as possible. Any help or suggestions would be appreciated. First - is multimon expecting a properly formatted AX.25 frame? (This code is clearly not creating an HDLC-encoded, AX.25-formatted frame). It looks like you're sending conventional async-encoded ASCII. You need to: That would explain it. I hadn't planned on using AX-25 (TX is the audio subcarrier of an FM ATV camera; no point in packetizing it since no digipeaters would be able to hear it, and unit should never be out of reception range in any case). I had planned on adding framing, checksums and possibly FEC later. I was also trying to keep it as simple as possible initially; encode raw octets and make sure I can decode them. It may be worth adding the HDLC/ax.25; initial basestation will likely be a laptop running soundmodem. Also - I'd avoid the use of transcendental functions (sin()) in the synthesizer loop ; I'd create a lookup table and index it via an integer phase accumulator. Off the top of my head, I'd guess a 16-bit phase accumulator and a 256-element sine array would be plenty. You'd need to precalculate the phase increments for 1200 and 2200 Hz, likely as constants. That is step 2 ![]() so conversion to integer is definately indicated. ![]() Thanks, R C KG4MVB |
#3
![]() |
|||
|
|||
![]()
Dana Myers K6JQ wrote in message ws.com...
R C wrote: I'm working on a telemetry board and wish to include software FSK1200 (MX-614's are getting harder to find). The official Bell 202 spec is out of print and hard to find as well, so I pieced this together from what I can find. Multimon won't decode the output, so I'm likely doing something wrong. This is supposed to be (once it works) my reference implementation before I port it to my telemetry board, so I'm trying to keep it as simple as possible. Any help or suggestions would be appreciated. First - is multimon expecting a properly formatted AX.25 frame? (This code is clearly not creating an HDLC-encoded, AX.25-formatted frame). It looks like you're sending conventional async-encoded ASCII. You need to: That would explain it. I hadn't planned on using AX-25 (TX is the audio subcarrier of an FM ATV camera; no point in packetizing it since no digipeaters would be able to hear it, and unit should never be out of reception range in any case). I had planned on adding framing, checksums and possibly FEC later. I was also trying to keep it as simple as possible initially; encode raw octets and make sure I can decode them. It may be worth adding the HDLC/ax.25; initial basestation will likely be a laptop running soundmodem. Also - I'd avoid the use of transcendental functions (sin()) in the synthesizer loop ; I'd create a lookup table and index it via an integer phase accumulator. Off the top of my head, I'd guess a 16-bit phase accumulator and a 256-element sine array would be plenty. You'd need to precalculate the phase increments for 1200 and 2200 Hz, likely as constants. That is step 2 ![]() so conversion to integer is definately indicated. ![]() Thanks, R C KG4MVB |
#4
![]() |
|||
|
|||
![]()
R C wrote:
Dana Myers K6JQ wrote in message ws.com... It may be worth adding the HDLC/ax.25; initial basestation will likely be a laptop running soundmodem. In which case, AX.25 isn't that hard at all to implement, HDLC is fairly trivial, and the checksum code can be found pretty readily (I have a Java port of something I sponged out of an old RFC if you're looking for sample code). You'll likely need it all for soundmodem recognize your frames, but you don't need to do full-on connected-mode AX.25, UI datagrams are likely enough. Also - I'd avoid the use of transcendental functions (sin()) in the synthesizer loop ; I'd create a lookup table and index it via an integer phase accumulator. Off the top of my head, I'd guess a 16-bit phase accumulator and a 256-element sine array would be plenty. You'd need to precalculate the phase increments for 1200 and 2200 Hz, likely as constants. That is step 2 ![]() so conversion to integer is definately indicated. ![]() Heh. You're tempting to cobble up the DDS code for giggles and flash it into an atMega16 I have sitting here ;-) Dana |
#5
![]() |
|||
|
|||
![]()
R C wrote:
Dana Myers K6JQ wrote in message ws.com... It may be worth adding the HDLC/ax.25; initial basestation will likely be a laptop running soundmodem. In which case, AX.25 isn't that hard at all to implement, HDLC is fairly trivial, and the checksum code can be found pretty readily (I have a Java port of something I sponged out of an old RFC if you're looking for sample code). You'll likely need it all for soundmodem recognize your frames, but you don't need to do full-on connected-mode AX.25, UI datagrams are likely enough. Also - I'd avoid the use of transcendental functions (sin()) in the synthesizer loop ; I'd create a lookup table and index it via an integer phase accumulator. Off the top of my head, I'd guess a 16-bit phase accumulator and a 256-element sine array would be plenty. You'd need to precalculate the phase increments for 1200 and 2200 Hz, likely as constants. That is step 2 ![]() so conversion to integer is definately indicated. ![]() Heh. You're tempting to cobble up the DDS code for giggles and flash it into an atMega16 I have sitting here ;-) Dana |
#6
![]() |
|||
|
|||
![]() |
#7
![]() |
|||
|
|||
![]() |
#8
![]() |
|||
|
|||
![]()
Dana Myers K6JQ wrote in message ws.com...
R C wrote: That is step 2 ![]() so conversion to integer is definately indicated. ![]() Heh. You're tempting to cobble up the DDS code for giggles and flash it into an atMega16 I have sitting here ;-) Hmm.. shot one, for GCC, no ASM. Nominally targetted for 8515 at 8 mhz, easy to redo. Not quite finished implementing, and definately not tested, but framework should be there. R C KG4MVB -- begin fsk2.c -- #include avr/io.h #include avr/pgmspace.h // Generated via sine.c. Theory suggests you can use reflexive nature of sine. // but we'll keep it simple. Besides, we have 8K of flash. // For avr use prog_uchar? prog_uchar s_table[] = [ 0x80,0x83,0x86,0x89,0x8C,0x90,0x93,0x96,0x99,0x9C, 0x9F,0xA2,0xA5,0xA8,0xAB,0xAE, 0xB1,0xB3,0xB6,0xB9,0xBC,0xBF,0xC1,0xC4,0xC7,0xC9, 0xCC,0xCE,0xD1,0xD3,0xD5,0xD8, 0xDA,0xDC,0xDE,0xE0,0xE2,0xE4,0xE6,0xE8,0xEA,0xEB, 0xED,0xEF,0xF0,0xF1,0xF3,0xF4, 0xF5,0xF6,0xF8,0xF9,0xFA,0xFA,0xFB,0xFC,0xFD,0xFD, 0xFE,0xFE,0xFE,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFD,0xFD,0xFC, 0xFB,0xFA,0xFA,0xF9,0xF8,0xF6, 0xF5,0xF4,0xF3,0xF1,0xF0,0xEF,0xED,0xEB,0xEA,0xE8, 0xE6,0xE4,0xE2,0xE0,0xDE,0xDC, 0xDA,0xD8,0xD5,0xD3,0xD1,0xCE,0xCC,0xC9,0xC7,0xC4, 0xC1,0xBF,0xBC,0xB9,0xB6,0xB3, 0xB1,0xAE,0xAB,0xA8,0xA5,0xA2,0x9F,0x9C,0x99,0x96, 0x93,0x90,0x8C,0x89,0x86,0x83, 0x80,0x7D,0x7A,0x77,0x74,0x70,0x6D,0x6A,0x67,0x64, 0x61,0x5E,0x5B,0x58,0x55,0x52, 0x4F,0x4D,0x4A,0x47,0x44,0x41,0x3F,0x3C,0x39,0x37, 0x34,0x32,0x2F,0x2D,0x2B,0x28, 0x26,0x24,0x22,0x20,0x1E,0x1C,0x1A,0x18,0x16,0x15, 0x13,0x11,0x10,0x0F,0x0D,0x0C, 0x0B,0x0A,0x08,0x07,0x06,0x06,0x05,0x04,0x03,0x03, 0x02,0x02,0x02,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x03,0x03,0x04, 0x05,0x06,0x06,0x07,0x08,0x0A, 0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x13,0x15,0x16,0x18, 0x1A,0x1C,0x1E,0x20,0x22,0x24, 0x26,0x28,0x2B,0x2D,0x2F,0x32,0x34,0x37,0x39,0x3C, 0x3F,0x41,0x44,0x47,0x4A,0x4D, 0x4F,0x52,0x55,0x58,0x5B,0x5E,0x61,0x64,0x67,0x6A, 0x6D,0x70,0x74,0x77,0x7A,0x7D ]; // Hmm.. for now assume an 8515-class running at 8 mhz. I've got a few // spares in the box, and haven't gotten to ordering the megas from digikey // yet. // This will not be a dedicated DDS, and it only needs to produce two // frequencies. Output methods include a DAC (R2R or separate), I2C digipot // (not sure if fast enough, but have some laying around) or PWM. Assume DAC // for now. // Choose an apropriate sample rate. ~10 khz should be adequate, and not put // a large burden on the AVR. A low pass may be indicated. // Keep things simple.. use 32-bit accumulator, so we can run on a slow clock. // This is overkill, but easier to do in GCC than 24-bit. volatile register uint32_t accum, freq_offset; /* May want to make a circular buffer */ volatile uint8_t fsk_out, fsk_bit, fsk_done_flag; // Okay.. 8M clock // Use /256 for TCNT0. 3 of the /256 cycles will give us a sample rate of // 10416 Hz #define TCNT0_PRESCALE 4 #define TCNT0_PERIOD 3 // FCLK = 10416/2^32 = 2.43x10^-6 resolution. // 1199.9999987893 hz #define MARK 494780232 // 2200.0000002057 hz #define SPACE 907097093 // Use /8 for TCNT1. This puts our nominal bitrate at 1199.4 bps, prolly close // enough. (0.05%) #define TCNT1_PRESCALE 2 #define BIT_PERIOD 667 SIGNAL(SIG_OUTPUT_COMPARE0) { // Not sure how well this will be optimized, but as it's only run // every 10khz.. accum += freq_offset; PORTC = PRG_RDB(s_table + (accum 24)); 0CR0 += TCNT0_PERIOD; // is this needed? sei(); } /* This needs to be set pretty close to 1/1500 second. */ SIGNAL(SIG_OUTPUT_COMPARE1A) { switch (fsk_bit) { case 0 : freq_offset = MARK; break; case 9 : freq_offset = SPACE; fsk_done = 1; break; default : freq_offset = (fsk_out (fsk_bit-1)) ? MARK : SPACE; break; } fsk_bit++; // If only item on TCNT1; //TCNT1 = 0; // If sharing TCNT1 OCR1A += BIT_PERIOD; // is this needed? sei(); } void main (void) { //setup everything, add ax.25 framing and HDLC encoding. optimized //crc16 in avr-libc. } |
#9
![]() |
|||
|
|||
![]()
Dana Myers K6JQ wrote in message ws.com...
R C wrote: That is step 2 ![]() so conversion to integer is definately indicated. ![]() Heh. You're tempting to cobble up the DDS code for giggles and flash it into an atMega16 I have sitting here ;-) Hmm.. shot one, for GCC, no ASM. Nominally targetted for 8515 at 8 mhz, easy to redo. Not quite finished implementing, and definately not tested, but framework should be there. R C KG4MVB -- begin fsk2.c -- #include avr/io.h #include avr/pgmspace.h // Generated via sine.c. Theory suggests you can use reflexive nature of sine. // but we'll keep it simple. Besides, we have 8K of flash. // For avr use prog_uchar? prog_uchar s_table[] = [ 0x80,0x83,0x86,0x89,0x8C,0x90,0x93,0x96,0x99,0x9C, 0x9F,0xA2,0xA5,0xA8,0xAB,0xAE, 0xB1,0xB3,0xB6,0xB9,0xBC,0xBF,0xC1,0xC4,0xC7,0xC9, 0xCC,0xCE,0xD1,0xD3,0xD5,0xD8, 0xDA,0xDC,0xDE,0xE0,0xE2,0xE4,0xE6,0xE8,0xEA,0xEB, 0xED,0xEF,0xF0,0xF1,0xF3,0xF4, 0xF5,0xF6,0xF8,0xF9,0xFA,0xFA,0xFB,0xFC,0xFD,0xFD, 0xFE,0xFE,0xFE,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFD,0xFD,0xFC, 0xFB,0xFA,0xFA,0xF9,0xF8,0xF6, 0xF5,0xF4,0xF3,0xF1,0xF0,0xEF,0xED,0xEB,0xEA,0xE8, 0xE6,0xE4,0xE2,0xE0,0xDE,0xDC, 0xDA,0xD8,0xD5,0xD3,0xD1,0xCE,0xCC,0xC9,0xC7,0xC4, 0xC1,0xBF,0xBC,0xB9,0xB6,0xB3, 0xB1,0xAE,0xAB,0xA8,0xA5,0xA2,0x9F,0x9C,0x99,0x96, 0x93,0x90,0x8C,0x89,0x86,0x83, 0x80,0x7D,0x7A,0x77,0x74,0x70,0x6D,0x6A,0x67,0x64, 0x61,0x5E,0x5B,0x58,0x55,0x52, 0x4F,0x4D,0x4A,0x47,0x44,0x41,0x3F,0x3C,0x39,0x37, 0x34,0x32,0x2F,0x2D,0x2B,0x28, 0x26,0x24,0x22,0x20,0x1E,0x1C,0x1A,0x18,0x16,0x15, 0x13,0x11,0x10,0x0F,0x0D,0x0C, 0x0B,0x0A,0x08,0x07,0x06,0x06,0x05,0x04,0x03,0x03, 0x02,0x02,0x02,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x03,0x03,0x04, 0x05,0x06,0x06,0x07,0x08,0x0A, 0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x13,0x15,0x16,0x18, 0x1A,0x1C,0x1E,0x20,0x22,0x24, 0x26,0x28,0x2B,0x2D,0x2F,0x32,0x34,0x37,0x39,0x3C, 0x3F,0x41,0x44,0x47,0x4A,0x4D, 0x4F,0x52,0x55,0x58,0x5B,0x5E,0x61,0x64,0x67,0x6A, 0x6D,0x70,0x74,0x77,0x7A,0x7D ]; // Hmm.. for now assume an 8515-class running at 8 mhz. I've got a few // spares in the box, and haven't gotten to ordering the megas from digikey // yet. // This will not be a dedicated DDS, and it only needs to produce two // frequencies. Output methods include a DAC (R2R or separate), I2C digipot // (not sure if fast enough, but have some laying around) or PWM. Assume DAC // for now. // Choose an apropriate sample rate. ~10 khz should be adequate, and not put // a large burden on the AVR. A low pass may be indicated. // Keep things simple.. use 32-bit accumulator, so we can run on a slow clock. // This is overkill, but easier to do in GCC than 24-bit. volatile register uint32_t accum, freq_offset; /* May want to make a circular buffer */ volatile uint8_t fsk_out, fsk_bit, fsk_done_flag; // Okay.. 8M clock // Use /256 for TCNT0. 3 of the /256 cycles will give us a sample rate of // 10416 Hz #define TCNT0_PRESCALE 4 #define TCNT0_PERIOD 3 // FCLK = 10416/2^32 = 2.43x10^-6 resolution. // 1199.9999987893 hz #define MARK 494780232 // 2200.0000002057 hz #define SPACE 907097093 // Use /8 for TCNT1. This puts our nominal bitrate at 1199.4 bps, prolly close // enough. (0.05%) #define TCNT1_PRESCALE 2 #define BIT_PERIOD 667 SIGNAL(SIG_OUTPUT_COMPARE0) { // Not sure how well this will be optimized, but as it's only run // every 10khz.. accum += freq_offset; PORTC = PRG_RDB(s_table + (accum 24)); 0CR0 += TCNT0_PERIOD; // is this needed? sei(); } /* This needs to be set pretty close to 1/1500 second. */ SIGNAL(SIG_OUTPUT_COMPARE1A) { switch (fsk_bit) { case 0 : freq_offset = MARK; break; case 9 : freq_offset = SPACE; fsk_done = 1; break; default : freq_offset = (fsk_out (fsk_bit-1)) ? MARK : SPACE; break; } fsk_bit++; // If only item on TCNT1; //TCNT1 = 0; // If sharing TCNT1 OCR1A += BIT_PERIOD; // is this needed? sei(); } void main (void) { //setup everything, add ax.25 framing and HDLC encoding. optimized //crc16 in avr-libc. } |
#10
![]() |
|||
|
|||
![]()
Paul Keinanen wrote in message . ..
On 4 Jul 2003 23:21:05 -0700, (R C) wrote: I'm working on a telemetry board and wish to include software FSK1200 (MX-614's are getting harder to find). The official Bell 202 spec is out of print and hard to find as well, so I pieced this together from what I can find. Multimon won't decode the output, so I'm likely doing something wrong. What is this Multimon thing expecting ? Okay, I've figured some things out since I first posted. Multimon was written by Thomas Sailer, who also wrote the soundmodem program. It's available at http://www.baycom.org/~tom/ham/linux/multimon.html. Documentation is lacking, but reviewing the source (don't try and debug at 4 AM; I should've figured this out originally) the afsk1200 decoder expects hdlc encoding, and possibly AX.25 framing (didn't trace that far). However, an undocumented option -v 10 yields the raw bit stream. In that case I assume you have mixed the polarity of the start and stop bits. In ordinary RS-232 asynchronous communication the start bit is "0" SPACE, interrupting the idle MARK state, followed by the data bits with LSB sent first. The stop bit is "1" or MARK, which then transfers to a MARK idle state if no more characters are to be transmitted. That part I figured out from APRS packets recorded off the air. Start is definately 1200 hz, stop is 2200 hz. Thanks for the suggestions, R C KG4MVB |
Reply |
|
Thread Tools | Search this Thread |
Display Modes | |
|
|
![]() |
||||
Thread | Forum | |||
Info needed on "Taylor Modulation" from the 50's. | Boatanchors | |||
Pye Modulation Meter on eBay | Boatanchors | |||
EBAY:MultiMatch MODULATION TRANSFORMER | Boatanchors | |||
for sale: UTC, VM-4, 300watt vari-tap modulation transformer | Boatanchors | |||
BIG MODULATION TRANSFORMER | Boatanchors |