Rev0Trac

From rev0wiki
Jump to: navigation, search

This is a project to create an APRS Tracker to transmit GPS coordinates and other information over a ham radio via the AX.25 protocol.

rev0Trac, hardware v1.1 connected to a GPS and battery.

Contents

Summary

APRS (Automatic Packet Reporting System) is a type of packet, or digital, radio protocol that enables users to transmit data such as position, weather, messages, and other information over a network of radios and repeaters called digipeaters. This allows users to tune in to the APRS frequency and within several minutes, know the locations of all stations within an area. This information can also be uploaded to the internet through systems called IGates. A short presentation on this project was given at the Cal Poly Amateur Radio Club's March 3rd general meeting, and can be found here: http://prezi.com/27z5ktni6jle/aprs-talk/

How does APRS work?

At the highest level, APRS is a packet radio protocol, meaning all data is formatted in packets following a defined standard, with each packet encoding information such as destination addresses, source address (your callsign), and error checking, along with the intended data.

This is the AX.25 UI frame (packet) format, as used in APRS[1]

First, let's define some packet fields.

How is an APRS packet sent?

APRS uses the modulation scheme of the Bell 202 modem, called AFSK (Audio Frequency-Shift Keying), at a data rate of 1200 baud (bits per second). This uses two audio tones of different frequencies to represent binary bits, with a 1200Hz tone as the "mark", or "1" bit, and a 2200Hz tone as the "space" or "0" bit. However, if data were simply sent using this method, there are certain strings of data that would create an unintentional flag (0b01111110), and may trick the receiver into thinking the packet has ended. To combat this problem, a scheme called bit stuffing is used. Any time a sequence of 5 or more consecutive "1"s are found in the data, a "0" is inserted, ensuring that no flags (sequence of 6 ones) are found anywhere but the beginning and end of a packet. Additionally, to assist with decoding of the packet, a scheme called NRZI (Non-Return to Zero Inverted) is applied to the data. This encodes a "0" as a change in state (i.e. from a "0" to a "1" or a "1" to a "0") and a "1" as no change in state. Together with bit stuffing, this ensures that at least every 5 bits, there is a change in state to aid in clock recovery. Note that unlike bit stuffing, NRZI is applied to the flags as well. Lastly, all bytes are sent LSB (least significant bit) first.

Hardware

Specifications

The rev0Trac v1.1 schematic.
Top/bottom view of the PCB for rev0Trac v1.1.

Rev0Trac is based primarily on an ATmega88PA microcontroller, chosen for ease of implementation based on the prototype (based on an ATtiny44A) and my experience with AVR microcontrollers, the USART for interfacing to a NMEA compliant GPS, and the 8kB of code space to allow for future additions to the project. The ATmega88PA generates audio tones via a 6-bit R-2R ladder, which is attenuated through 2 fixed resistors (which set the upper and lower output voltage limits) and an SMD trim potentiometer. This signal is then low-pass filtered and buffered through an MCP6001 op-amp, and output through a DC-blocking capacitor and 2.2k ohm resistor.

The ATmega88PA interfaces directly with a GPS module via its built in USART module and a 4-pin header on the top side of the board. It receives NMEA strings and parses the data out of the GGA sentence to update outgoing APRS packets with.

Audio is received, amplified, and low-pass filtered, and sent to the analog comparator module of the ATmega88PA. Audio levels on the input are centered about 1/2 Vcc, and can be compared to any of 32 voltages, including 1/2Vcc, Vcc, GND, Vcc + 1/32Vcc, etc., by assigning the topmost R-2R DAC pin as an analog input, selecting it as the comparator negative input, and generating a voltage with the 5 remaining R-2R DAC pins. Using this method, tones can be detected by counting the time between zero crossings of the input waveform.

Power to the unit is provided by either a CR1220 3V battery (this will only be used for testing/demonstration) or through a 2-pin header. Input voltage can range from 4.5-24V, and is regulated to 3.3V by an LM3480.

Bill of Materials

IC's Quantity Unit Cost Total Cost Source Part Number
ATmega88PA Microcontroller 1 $3.57 $3.57 Digi-Key ATMEGA88PA-AU-ND
MCP6001 Op-Amp 2 $0.29 $0.58 Digi-Key MCP6001T-I/OTCT-ND
LM3480 3.3V Voltage Regulator 1 $1.10 $1.10 Digi-Key LM3480IM3-3.3CT-ND
Discrete Semiconductors Quantity Unit Cost Total Cost Source Part Number
BC817 NPN Bipolar Transistor 1 $0.33 $0.33 Digi-Key 568-1631-1-ND
Passives - Capacitors Quantity Unit Cost Total Cost Source Part Number
0.47uF 0603 Ceramic Capacitor 3 (Qty. 10) $0.055 $0.55 Digi-Key 445-1320-1-ND
0.1uF 0603 Ceramic Capacitor 3 (Qty. 10) $0.02 $0.20 Digi-Key 445-1316-1-ND
3.3nF 0603 Ceramic Capacitor 1 (Qty. 10) $0.024 $0.24 Digi-Key 445-5084-1-ND
20pF 0603 Ceramic Capacitor 2 (Qty. 10) $0.036 $0.36 Digi-Key 445-5052-1-ND
Passives - Resistors Quantity Unit Cost Total Cost Source Part Number
2k 0603 1% Resistor 9 (Qty. 10) $0.025 $0.25 Digi-Key RMCF0603FT2K00CT-ND
2.2k 0603 Resistor 2 $0.04 $0.08 Digi-Key RMCF0603FT2K20CT-ND
1k 0603 1% Resistor 6 (Qty. 10) $0.025 $0.25 Digi-Key RMCF0603FT1K00CT-ND
10k 0603 1% Resistor 2 (Qty. 10) $0.04 $0.08 Digi-Key RMCF0603FT10K0CT-ND
100k 0603 1% Resistor 4 $0.04 $0.16 Digi-Key RMCF0603FT100KCT-ND
1M 0603 Resistor 1 $0.02 $0.02 Digi-Key RMCF0603JT1M00CT-ND
100R 0603 Resistor 1 $0.02 $0.02 Digi-Key RMCF0603JT100RCT-ND
330R 0603 Resistor 2 $0.02 $0.04 Digi-Key RMCF0603JT330RCT-ND
Other Quantity Unit Cost Total Cost Source Part Number
4 Connector 2.5mm Audio Jack 1 $0.96 $0.96 Digi-Key CP1-42534SJCT-ND
9.8304MHz Crystal 1 $0.63 $0.63 Digi-Key 300-8425-ND
16 Pin 0.100" Male Header 1 $0.87 $0.87 Digi-Key WM6416-ND
10k Single Turn 2mm SMD Potentiometer 1 (Qty. 5) $0.152 $0.76 Digi-Key 490-2032-1-ND
Misc. Quantity Unit Cost Total Cost Source Part Number
Shipping 1 $5.49 $5.49 Digi-Key N/A
PCB 1.187 in2 (Qty. 3) $5.00 $5.94 Laen/Dorkbot PDX PCB Order N/A
Total Price ~$22.48

PCB/Construction Photos

Software

The program can be broken down into two main components: GPS/NMEA parser and APRS encoder.

NMEA Parser

The following code runs every time a serial byte is received by the USART module:

  1. //In main.c
  2. ISR(USART_RX_vect)
  3. {
  4.    byteRX = USART_Receive();
  5.  
  6.    if(byteRX == '$') //$ = Start of NMEA Sentence
  7.    {
  8.       gpsindex = 0;
  9.       done = 0;
  10.    }
  11.    else if(byteRX == 0x0D) //<CR> = End of Transmission
  12.    {
  13.       done = 1;
  14.  
  15.       if(gpsdata[4] == 'G') //Make sure this is a GGA sentence
  16.       {         
  17.          parse_nmea();
  18.       }
  19.    }
  20.    if(done != 1)
  21.    {
  22.       gpsdata[gpsindex] = byteRX;
  23.       gpsindex++;
  24.    }
  25. }

First, the byte is read in from the receive register. If the byte is a '$', it marks the beginning of a NMEA sentence, so the index of the gpsdata[] array is set to 0. If the byte is a carriage return (0x0D), then it marks the end of the NMEA sentence, so the done flag is set high to stop accepting data into the array. The fourth byte of the gpsdata[] array is checked, if it is a 'G', it must be a "$GPGGA" sentence, so the parse_nmea() function is called. Otherwise, bytes are filled into the gpsdata[] array.

The following code parses the relevant data out of the "$GPGGA" sentence held in the gpsdata[] array:

  1. //In gps.c
  2. void parse_nmea(void)
  3. {
  4.    int i = 0, n;
  5.  
  6.    for(n=0;n<MAXCOMMAS;n++) //Find the positions of all commas in the NMEA sentence, put positions in commas[]
  7.    {
  8.       for(;gpsdata[i]!=0x2C;i++); //Find next comma; continue stepping through the array until we find 0x2C (,)
  9.       commas[n] = i; //Store the index in commas[] array
  10.       i++;
  11.    }
  12.  
  13.    if(gpsdata[commas[5]+1] != 0x30) //Make sure we have GPS fix; 0 = invalid
  14.    {    
  15.       for(i=commas[1]+1;i<commas[2];i++)
  16.       {
  17.          lat[i-(commas[1]+1)] = gpsdata[i]; //Load latitude into lat[] array from stored NMEA string
  18.       }
  19.       ns = gpsdata[commas[2]+1];
  20.  
  21.       for(i=commas[3]+1;i<commas[4];i++)
  22.       {
  23.          lng[i-(commas[3]+1)] = gpsdata[i]; //Load longitude into lng[] array from stored NMEA string
  24.       }
  25.       ew = gpsdata[commas[4]+1];
  26.  
  27.       for(i=commas[0]+1;i<commas[1];i++)
  28.       {
  29.          time[i-(commas[0]+1)] = gpsdata[i]; //Load time into time[] array from stored NMEA string
  30.       }
  31.       valid = 1;
  32.  
  33.       update_packet(); //Update the packet with new good GPS data
  34.    }
  35.    else //Else update the timestamp, but retain old GPS data
  36.    {
  37.       for(i=commas[0]+1;i<commas[1];i++)
  38.       {
  39.          time[i-(commas[0]+1)] = gpsdata[i];
  40.       }
  41.       valid = 0;
  42.  
  43.       update_packet();
  44.    }
  45. }

This function begins by storing the positions of all commas in the NMEA sentence, which separate values in a standard order. From this information, the function then separates out the latitude, longitude, E/W and N/S statuses, and the time. If the GPS has a valid lock (noted in one of the data fields), the APRS packet is updated with the new location and time. Otherwise, only the timestamp is updated, with an 'I' following the timestamp to indicate a lack of GPS lock.

The following code updates the APRS packet with GPS data:

  1. //In gps.c
  2. void update_packet(void) //This function updates the APRS packet with GPS information
  3. {
  4.    int i;
  5.  
  6.    for(i=LATSTART;i<NSSTART;i++)
  7.    {
  8.       data[i] = lat[i-LATSTART]; //Copy latitude into packet at position LATSTART
  9.    }
  10.    data[i] = ns;
  11.    for(i=LONGSTART;i<EWSTART;i++)
  12.    {
  13.       data[i] = lng[i-LONGSTART]; //Copy longitude into packet at position LONGSTART
  14.    }
  15.    data[i] = ew;
  16.    for(i=CMTSTART;i<(TIMEEND+1);i++)
  17.    {
  18.       data[i] = time[i-CMTSTART]; //Copy time into packet at position CMTSTART
  19.    }
  20.    if(valid) //If GPS coordinates are valid, put a V after the timestamp
  21.    {
  22.       data[(TIMEEND+1)] = 'V';
  23.    }
  24.    else //Else put an I after the timestamp
  25.    {
  26.       data[(TIMEEND+1)] = 'I';
  27.    }
  28.  
  29.    if(delay == DELAYTIME) //If DELAYTIME seconds have passed, begin packet transmission
  30.    {
  31.       start();
  32.       delay = 1;
  33.    }
  34.    else //Else continue counting up
  35.    {
  36.       delay++;
  37.    }
  38. }

The appropriate sections of the APRS data[] array are updated with the latitude, longitude, E/W, N/S, and timestamp data, as well as a flag that indicates valid GPS lock. If DELAYTIME cycles of this function call have passed, then the APRS packet is transmitted.

APRS Encoder

The following functions are used to return a single bit from the packet, start the transmission, and end the transmission, respectively:

  1. char data_bit(char byte, char bit)
  2. {
  3.    return ((data[(int)byte]>>bit) & 0x01);
  4. }
  5. void start(void)
  6. {
  7.    UCSR0B = 0x00; //Disable USART Receiver, disable receive interrupt
  8.    sbi(PORTB,PTT);
  9.    _delay_ms(D_KEY);
  10.    fcs = 0xFFFF;
  11.    mark = data_bit(0,0);
  12.    bitindex = 1;
  13.    TCCR0B = 0b00000010; //Timer on
  14. }
  15.  
  16. void end(void)
  17. {
  18.    TCCR0B = 0b00000000; //Timer off
  19.    PORTC = 32; //Output low
  20.    byteindex = 0;
  21.    ended = 0;
  22.    n = 0;
  23.    counter = 0;
  24.    _delay_ms(D_UNKEY);
  25.    cbi(PORTB,PTT);
  26.    UCSR0B = (1<<RXEN0)|(1<<RXCIE0); //Enable USART Receiver, enable receive interrupt
  27. }

The following code updates the frame check sequence bytes after every bit is transmitted:

  1. void fcs_update(void)
  2. {
  3.    unsigned char shiftbit = 0x0001 & fcs; //Store bit rotated off in variable shiftbit
  4.    fcs = fcs >> 1; //Shift fcs right by 1
  5.    if(shiftbit != data_bit(byteindex,bitindex)) //If shiftbit doesn't match the data being sent, xor with 0x8408
  6.    {
  7.       fcs ^= 0x8408;
  8.    }
  9. }

APRS uses the AX.25 protocol, which specifies that the end of the packet is occupied by a 16-bit frame check sequence, generated according to the CRC-CCITT format, with a polynomial of 0x8408. The CRC algorithm first begins with a 16-bit word of 0xFFFF. For each bit between the end of the first bit of the destination address and the last bit of the information field, the function shifts the CRC word right, and compares the bit shifted off with the bit being sent. If the bit being sent does not match the bit that was shifted, then the CRC word is XORed with the polynomial, 0x8408. When the FCS is sent, the word is inverted (XORed with 0xFFFF) and sent low-byte first.

The following code is run 33*1200 times per second, and is responsible for the actual transmitting of bits and the handling of the low-level packet protocol:

  1. ISR(TIMER0_COMPA_vect)
  2. {
  3.    if(mark == 1)
  4.    {
  5.       PORTC = coslow[n];   //Output next sample of a 1200Hz cosine
  6.    }
  7.    else
  8.    {
  9.       PORTC = coshigh[n];  //Output next sample of a 2200Hz cosine
  10.    }
  11.  
  12.    counter++;
  13.    n++;
  14.  
  15.    if(mark == 0)
  16.    {
  17.       if(n>32)
  18.          n = 15;
  19.    }
  20.    else
  21.    {
  22.       if(n>32)
  23.          n = 0;
  24.    }
  25.  
  26.    if((ended == 1) && (counter > 32))
  27.    {
  28.       end();
  29.    }
  30.  
  31.    if(counter > 32)
  32.    {
  33.       counter = 0;
  34.       //Change next sent tone according to NRZI (non-return to zero inverted)
  35.       if((data_bit(byteindex,bitindex) == 0))
  36.       {
  37.          onecount = 1;
  38.          mark ^= 0x01; //If the next bit is zero, toggle the output, else no change
  39.  
  40.          n--;
  41.          if(mark == 0)
  42.          {
  43.             n += phslh[n]; //Last value was 1, add phase shift for 1200Hz to 2200Hz
  44.             if(n>32)
  45.                n = 14;  //If 2200Hz index overflows, start at position 15
  46.          }
  47.          else
  48.          {
  49.             n += phshl[n]; //Last value was 0, add phase shift for 2200Hz to 1200Hz
  50.             if(n>32)
  51.                n = -1;   //If 1200Hz index overflows, start at position 0
  52.          }
  53.          n++;
  54.       }
  55.       else if((data_bit(byteindex,bitindex) == 1))
  56.       {
  57.          onecount++;
  58.  
  59.          if(data[(int)byteindex] == 0x7E)
  60.          {
  61.             onecount = 1;
  62.          }
  63.          else if(onecount > 5)
  64.          {
  65.             stuff = 2;
  66.             onecount = 1;
  67.          }
  68.       }
  69.  
  70.       if(stuff > 0 && stuff != 3)
  71.       stuff--;
  72.  
  73.       if(stuff > 0)
  74.       {
  75.          if(byteindex < (NUMBYTES - NUMFLAGS - 2) && byteindex > NUMPREAMB)
  76.             fcs_update();
  77.          bitindex++;
  78.       }
  79.       else
  80.       {
  81.          stuff = 3;
  82.  
  83.          if((data_bit(byteindex,bitindex) == 1))
  84.          {
  85.             mark ^= 0x01;
  86.  
  87.             n--;
  88.             if(mark == 0)
  89.             {
  90.                n += phslh[n]; //Last value was 1, add phase shift for 1200Hz to 2200Hz
  91.                if(n>32)
  92.                   n = 14;  //If 2200Hz index overflows, start at position 15
  93.             }
  94.             else
  95.             {
  96.                n += phshl[n]; //Last value was 0, add phase shift for 2200Hz to 1200Hz
  97.                if(n>32)
  98.                   n = -1;   //If 1200Hz index overflows, start at position 0
  99.             }
  100.             n++;
  101.          }
  102.       }
  103.  
  104.       if(bitindex>7)
  105.       {
  106.          bitindex = 0;
  107.          byteindex++;
  108.          if(byteindex == (NUMBYTES - NUMFLAGS - 2))
  109.          {
  110.             fcs ^= 0xFFFF;
  111.             data[(NUMBYTES - NUMFLAGS - 1)] = (char)((fcs >> 8) & 0x00FF);
  112.             data[(NUMBYTES - NUMFLAGS - 2)] = (char)(fcs & 0x00FF);
  113.          }
  114.          else if(byteindex == NUMBYTES)
  115.          {
  116.             ended = 1;
  117.          }
  118.       }
  119.    }
  120. }

The code can be broken down into 3 sub-parts:

Per-sample Code

The beginning of the code, lines 258-268, simply set the output (PORTC, the R-2R ladder) to the value given in the cosine lookup tables for the appropriate waveform (1200 or 2200Hz). Then the sample index and sample counter are incremented.

Lines 270-279 check for sample index overflow. If overflow has occurred, then n is reset to the zero or beginning point for the waveform, index 0 for the 1200Hz or index 15 for the 2200Hz wave.

Line 281-284 check if the end condition has been met, and if so, calls the end() function, which shuts down the timer, unkeys the radio, and resets indices and other variables to their starting values.

Per-bit Code

This code is run after 32 samples of a waveform have been output. The counter is reset to 0, and one of two blocks of code are run, depending on whether the bit to be transmitted is a '1' or a '0'.

Lines 290-309 are run if the next bit to be transmitted is a '0'. According to NRZI encoding, the frequency of the output must be changed (from either 1200 to 2200Hz or 1200 to 2200Hz). The onecount variable (discussed below) is reset to 1, the mark variable (which determines whether or not the output waveform is 1200 or 2200Hz) is toggled, and the index of the new sample waveform is updated with the phase difference held in the appropriate phase difference arrays.

Lines 310-323 are run if the next bit to be transmitted is a '1'. According to NRZI encoding, the frequency of the output remains unchanged, but according to bit-stuffing, we must account for the number of consecutive ones transmitted. The onecount variable is incremented, unless the byte being transmitted is a flag (0x7E), in which case it is reset to 1. If the onecount variable exceeds 5, then the stuff variable is set to 2, which inserts a zero in the output, through the code below.

Per-byte Code

When the bit index is greater than 7, an entire byte has been transmitted. The bit index is reset to 0, the byte index is incremented, and the code checks to see if it is time to transmit the FCS bytes, or if the packet is done being sent, sets the end condition bit.

The FCS must be inverted (XORed with 0xFFFF) and byte swapped, then transmitted like other bytes, LSB first.

Files

Here are the latest project files:

To-do List

Changelog

** Indicates unreleased version

Results

Here is the first successful set of APRS transmissions of GPS position from rev0Trac v1.1:

CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.33W>103701
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.72N/11947.32W>103710
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.31W>103719
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.31W>103727
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.30W>103735
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.31W>103742
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.32W>103751
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.33W>103759
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.72N/11947.33W>103807
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.72N/11947.34W>103814
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.72N/11947.34W>103821
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.72N/11947.35W>103829
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.36W>103836
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.37W>103843
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.37W>103850
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.38W>103858
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.38W>103906
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.38W>103913
CQ de KJ6KST-1 via WIDE2-2 Ctl R UI Pid=F0 Len=26>
!3425.71N/11947.38W>103921

Note, some time after 10:38.50 UTC, GPS lock was lost, so the tracker continues transmitting the last known good coordinates, with updated timestamps.

Here is the flight path transmitted by a modified version of rev0Trac as flown on the IEEE High Altitude Balloon Project:

Path recorded by the three ground stations tracking the IEEE HAB.

The .kml file can be found here: http://rev0proto.com/files/APRS-Full.kml

The hardware functioned independently from the XBee/Propeller, which was sending updates including sensor data at a higher rate alongside it. The tracker worked throughout the flight, transmitting via a Puxing PX-2R 2W UHF transceiver, and receiving its GPS coordinates from a PMB-688 GPS module (which stopped updating coordinates while the balloon was above 24,026 meters in altitude).

During early testing, the rev0Trac appeared to have significant issues when in close proximity to a transmitting radio, the results of which can be seen by this oscilloscope shot showing the radio beginning a transmission:

RF Interference problem. Left frame: radio begins transmitting. Right frame: early packet termination due to interference.

Ideas for Future Revisions

I would like to add the following features:

Tools and Files

R-2R DAC Simulation

Simulation of the R-2R DAC, feeding voltage back into an ADC input.

The R-2R DAC and analog comparator negative input can be simulated with the following text file, which can be imported into the Falstad.com Circuit Simulator Applet, found here: http://www.falstad.com/circuit/

(Copy text and paste into Import dialog in Circuit Simulator Applet) http://www.rev0proto.com/files/falstad_6-bit_tri-state_r2r.txt

Min/Max Resistor Voltage Divider Calculator

Schematic for a voltage divider circuit with settable min/max.
Example values for the voltage divider calculator.

To generate a variable output voltage with minimum and maximum limits, I used the circuit shown to the right, and derived a formula to calculate the corresponding resistor values. For example, if you wanted to generate a reference voltage for a circuit that could be adjusted between 2.4V and 2.6V, given a 5V reference, you would type those limits into the lower part of the calculator, and the input voltage, in the blue boxes, specifying the resistance of the potentiometer, let's say 10k ohms. The calculator gives us a result of 86.06k ohms for the lower resistor, and 83.23k ohms for the upper resistor. Since we can't obtain those exact values, you can plug in the values of the resistors you have in the upper blue boxes, for example, the standard 10% value of 82k ohms gives us an upper limit of 2.644V and lower limit of 2.356V.

http://www.rev0proto.com/files/voltdiv_minmax.xls

PWL Function Generator (Work in progress)

Pwlgen output in action.

In order to more accurately simulate the effectiveness of the low-pass filter at rejecting high-frequency components of the DAC output, I used a C program to generate PWL text files for use with LTspice IV. I hope to expand the functionality of the program and rewrite it with a graphical interface, to allow it to serve as an arbitrary waveform generator for LTspice. Currently, the program is only configured to generate discrete cosines, and uses the rise time value for both rise and fall times of the waveform.

Usage:

Executable: http://www.rev0proto.com/files/pwlgen.zip
Example input text file: http://www.rev0proto.com/files/in.txt
Source code: http://www.rev0proto.com/files/pwlgen.c

Eagle Schematic and Board Files

Schematic: http://www.rev0proto.com/files/aprs-2side_v11.sch
Board: http://www.rev0proto.com/files/aprs-2side_v11.brd

References

  1. The APRS Working Group, "APRS Protocol Reference". Available: http://www.aprs.org/doc/APRS101.PDF [Accessed 11/20/2011].
  2. APRSWiki, "Symbols and SSIDs". Available: http://info.aprs.net/index.php?title=SymbolsAndSSIDs [Accessed 11/20/2011].
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox