Reply
 
LinkBack Thread Tools Search this Thread Display Modes
  #1   Report Post  
Old July 5th 03, 07:21 AM
R C
 
Posts: n/a
Default Bell 202 modulation (AFSK1200)

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   Report Post  
Old July 5th 03, 05:10 PM
R C
 
Posts: n/a
Default

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 The target CPU will most likely be a Mega8535 AVR,
so conversion to integer is definately indicated.

Thanks,
R C
KG4MVB
  #3   Report Post  
Old July 5th 03, 05:10 PM
R C
 
Posts: n/a
Default

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 The target CPU will most likely be a Mega8535 AVR,
so conversion to integer is definately indicated.

Thanks,
R C
KG4MVB
  #4   Report Post  
Old July 6th 03, 01:52 AM
Dana Myers K6JQ
 
Posts: n/a
Default

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 The target CPU will most likely be a Mega8535 AVR,
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   Report Post  
Old July 6th 03, 01:52 AM
Dana Myers K6JQ
 
Posts: n/a
Default

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 The target CPU will most likely be a Mega8535 AVR,
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



  #8   Report Post  
Old July 7th 03, 02:36 AM
R C
 
Posts: n/a
Default

Dana Myers K6JQ wrote in message ws.com...
R C wrote:
That is step 2 The target CPU will most likely be a Mega8535 AVR,
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   Report Post  
Old July 7th 03, 02:36 AM
R C
 
Posts: n/a
Default

Dana Myers K6JQ wrote in message ws.com...
R C wrote:
That is step 2 The target CPU will most likely be a Mega8535 AVR,
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   Report Post  
Old July 7th 03, 03:46 AM
R C
 
Posts: n/a
Default

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
Search this Thread:

Advanced Search
Display Modes

Posting Rules

Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
Info needed on "Taylor Modulation" from the 50's. Mike Baker Boatanchors 3 January 10th 06 07:55 PM
Pye Modulation Meter on eBay EnTee Boatanchors 0 November 14th 03 02:58 PM
EBAY:MultiMatch MODULATION TRANSFORMER W3ATV Boatanchors 0 August 22nd 03 04:54 PM
for sale: UTC, VM-4, 300watt vari-tap modulation transformer Ax9909 Boatanchors 0 August 3rd 03 12:06 AM
BIG MODULATION TRANSFORMER Jim Moci Boatanchors 0 August 2nd 03 04:07 PM


All times are GMT +1. The time now is 09:37 PM.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Copyright ©2004-2024 RadioBanter.
The comments are property of their posters.
 

About Us

"It's about Radio"

 

Copyright © 2017