Esta página aún no está traducida; se muestra en inglés.

APRS

APRS (Automatic Packet Reporting System) on RadioFoundry: our position, status and messages as AX.25 UI frames in Bell 202 AFSK on FM; positions over DMR to an APRS gateway ID; automatic beaconing from several trigger sources; a KISS TNC on the host port (and on TCP port 8001 in USB network mode); the iGate rules, with the APRS-IS connection held by a host helper or by the radio's own client (tcp-services.md 4, 7); and, on radios that can sample their received audio, a demodulator, a station list, messages to us and a digipeater.

Code: core/aprs (portable, host tested), the RF part in core/radio/radio_sm.c (the APRS block), the board's tone path behind hal/hal_afsk.h, the UI in core/ui/ui_aprs.c, the debug link in core/debuglink/debuglink_aprs.c, the host helper tools/aprs_gate.py. Build switches: CONFIG_APRS, CONFIG_APRS_RX, CONFIG_APRS_IGATE, and on the USB network link CONFIG_NET_KISS and CONFIG_NET_APRSIS (docs/build-config.md). The DMR GPS Info LC, which shares the position source, is in dmr-voice.md 3.1.

Sources (all public, read for the formats only; no implementation was used as a source, CLEANROOM.md):

  • AX.25 Link Access Protocol for Amateur Packet Radio, version 2.2 (TAPR, 1998): address field, UI frames, FCS, bit stuffing (sections 3, 6).
  • APRS Protocol Reference 1.0 (APRS Working Group, 2000), "APRS101", and the APRS 1.1 and 1.2 addenda: data types, positions (plain and compressed), Mic-E, status, messages and acks, the third-party format, tocalls, paths, position ambiguity.
  • The KISS TNC protocol (Chepponis and Karn, 1987): framing, escapes, the command bytes.
  • aprs-is.net: "IGate Design", "q Construct", "Connecting to APRS-IS" (login, passcode, filters).
  • SmartBeaconing as published by Tony Arnerich KD7TA and Steve Bragg KA9MVA (HamHUD), released for anyone to use.
  • BrandMeister's APRS documentation: the location service IDs (the retired 5050-5059 and the MCC999 gateway) that turn Motorola (LRRP) and Hytera position reports into APRS positions ("Position reports of Hytera and Motorola radios can be forwarded to APRS", wiki.brandmeister.network/index.php/APRS, last edited 2020-07-03).
  • Bell 202: 1200 baud, mark 1200 Hz, space 2200 Hz, NRZI (a 0 bit is a tone change).

Black-box oracles (never read for code): Direwolf decodes the WAV that aprsTest --wav writes (tests/host/aprs_wav_check.sh) and the radio's transmission on the SDR bench (section 13); APRSdroid, Xastir or aprx can stand in.

1. What each radio does

Feature Needs
FM AFSK transmit board.afsk->tx_* (5.1)
FM AFSK receive, station list, digipeater, messages to us CONFIG_APRS_RX and receive audio: board.afsk->rx_*, or FM receive audio on the PCM stream (AUDIO_TAP_FM_RX) (5.3)
Positions over DMR (LRRP / UDT) a DMR radio, on a DMR channel
KISS TNC on the host port (UDP 3334 and TCP 8001 in network mode) nothing more (TX path; RX frames only with RX)
iGate RX for RF -> IS; IS -> RF messages only once RX exists
GPS CAP_GPS boards feed aprs_nmea(..., gps=true); otherwise host NMEA or the fixed position. CONFIG_GPS=n leaves out the board's GPS driver and CAP_GPS, so the radio behaves as one without a receiver. Settings > Location > GPS (settings_t.gps, default off) goes through ui_svc_apply at start-up and on each change to board_t.gps_enable (the module's power; the board probes the module at start-up whatever the setting says, so board_has_gps() stays true) and to aprs_gps_enable: off drops the board fix at once and ignores later sentences, so the position falls back to the computer or the fixed one

What a board provides:

Path AFSK transmit AFSK receive GPS
RF chip with a tone generator (AT1846S) The tone generator retuned per symbol FM receive audio on the PCM stream (AUDIO_TAP_FM_RX, 5.3) CAP_GPS
HR_C7000 8 kHz PCM through the modem's prompt FIFO into the FM modulator (modem-hrc7000.md 8.4) The modem's FM receive audio, ungated (8.5) CAP_GPS
No tone path written None (DMR positions still work) None CAP_GPS

feature-matrix.md shows which radios have AFSK transmit (APRS) and receive (APRS_RX).

Everything is host tested. On the SDR bench (section 13): positions over DMR (LRRP and UDT) decode, and FM AFSK decodes with a valid FCS.

2. Model

The engine (core/aprs/aprs.c) runs in the radio task: aprs_tick from radio_sm_poll every 25 ms. The UI, the debug link and the host link reach it through the "any task" calls, which take a lock the radio task sets up (aprs_set_lock, the scheduler suspended for short copies). Settings and codeplug reads take the storage mutex and so happen outside that lock.

Pure parts, each with host tests in tests/host/aprsTest.c: ax25.c (addresses, frames, TNC2 text), hdlc.c (FCS, bit stuffing, NRZI symbols, the receive deframer), afsk.c (the tone plan for TX, a correlator demodulator with a PLL for RX), kiss.c, aprs_fmt.c (APRS formats in and out), aprs_nmea.c, aprs_beacon.c (when to send), aprs_igate.c (gating and digipeater rules). tests/host/aprsRadioTest.c runs the engine with the radio core on a fake board whose AFSK output is demodulated back to frames.

3. Rules

  • Transmit is off by default. Automatic beacons need Tools > APRS > Automatic beacons; "Send position now" works without it.
  • A callsign is required: the debug link's test call, else Settings > My radio's radio name, else the codeplug's radio name; its first word, upper case, 3-6 letters and digits with a digit after the first character, not N0CALL. Our SSID comes from the APRS settings (7 by default). No callsign: nothing goes out, the page says why.
  • RF only on amateur frequencies: the APRS frequency must be in 144-148, 222-225, 420-450 or 902-928 MHz and in a band the board transmits in; else the reason is "not amateur" and nothing is keyed.
  • A hard minimum of 30 s between automatic beacons (AUTO_MIN_S), 10 s for any configuration (APRS_MIN_INTERVAL_FLOOR_S), and "send now" pressed again within 5 s is ignored.
  • Frames from the host (KISS) must carry our callsign (any SSID) as the source and are rate limited: a burst of 3, then one per 10 s.
  • CSMA before every RF transmission (section 6); our own PTT always wins (an APRS transmission is aborted, the channel restored).
  • iGate transmit (IS -> RF) only for messages to stations heard directly in the last 30 minutes (section 9.1).

4. Frames

4.1 What we send

  • Position: ! (or = when messaging is on), plain DDMM.mmN/DDDMM.mmW with the symbol, or compressed (base 91, the default), course / speed when known, altitude /A= when known, then the comment (the codeplug APRS record's comment if any). Position ambiguity 0-4 blanks digits as APRS101 chapter 8 says.
  • Status: >text (40 characters).
  • Messages: :ADDRESSEE:text{NN (67 characters), acks :ADDRESSEE:ackNN; ours retried at 30, 60 and 120 s (3 tries) until the ack. The composer can omit the message number (one transmission, no retry or acknowledgment) and append a position snapshot as @DD.DDDDN DDD.DDDDW in the message text. Both are per-message choices; requesting an acknowledgment is on and including position is off when the composer opens. A position needs a current GPS, host or fixed source. Its text counts toward the 67-character limit; a message that cannot fit is refused. The send API accepts printable ASCII text and rejects control bytes. An incoming rej stops retries without reporting the message as acknowledged.
  • Messages > New APRS message appears when APRS is built and our callsign is set. A station in Stations heard has a Message action that fills its callsign. The composer asks for an AX.25 callsign with optional SSID, then sends a localized preset or typed text through aprs_send_message. Presets fold accented letters to printable ASCII; typed text holds at most 67 characters. The callsign editor holds at most nine characters (six for the call, a hyphen and up to two for the SSID). Received APRS messages appear in Messages; replies use APRS. DMR replies use DMR, and Settings > Connections > Message format selects only a DMR encoding. A new outgoing APRS message replaces one still awaiting acknowledgment. aprs_send_message refuses a message when the RF path is blocked.
  • Path: none, WIDE1-1, WIDE1-1,WIDE2-1 (the default), WIDE2-1, WIDE2-2. Set on the APRS page (section 10), over the debug link, or in the codeplug's APRS record (section 11).

4.2 Tocall

Destination APZRDF: the APZ block is the experimental range anyone may use. A registered tocall (APxxxx from the tocalls list) is an open decision.

4.3 Received (RX builds)

Positions (plain, compressed, with and without timestamps), Mic-E (the destination field's latitude and message bits, longitude, speed and course from the information field), objects, items, status, messages, third-party packets, queries, weather and telemetry recognized by type (not decoded further).

5. The radio side

5.1 hal_afsk

hal/hal_afsk.h: tx_start(tones, nbits, baud, mark, space, level) plays a symbol string (one bit per symbol, 1 = mark), tx_busy, tx_stop; rx_rate_hz, rx_start, rx_read, rx_stop deliver received audio samples. board.afsk is NULL on boards without a tone path. A board that can only receive sets the rx_* members and leaves tx_start NULL. Without a transmit path the reason is "no AFSK" and only DMR positions go out.

5.2 Transmit

The radio core keys the transmitter in FM, then the board switches the tone at each symbol boundary (a timer at the baud rate), with phase continuity where the hardware allows, and keeps the RTOS out of its symbol interrupt. While AFSK runs, the radio core leaves the transmitter alone (status() returns its cached reading). The level (0-100, 50 default) sets the deviation around the board's nominal (about 3 kHz peak at 25 kHz bandwidth). tests/hil/test_aprs.py measures on air the phase continuity across a tone change, the deviation the level gives, the mark / space flatness and the TX delay (section 13). Each board's tone path is on its hardware page (the table in section 1).

5.3 Receive

Received FM audio comes from one of two places (aprs.c, rx_service):

  1. The board's own AFSK receive path: hal_afsk_t.rx_start, rx_read, rx_stop at rx_rate_hz. The HR_C7000 boards have one (shared hrc_afsk): the modem's FM receive audio at 8 kHz, not squelch gated (modem-hrc7000.md 8.5). While it runs the speaker plays FM through the HR_C7000 instead of straight from the RF chip.
  2. Otherwise FM receive audio on the board's PCM stream, when its audio tap has AUDIO_TAP_FM_RX (hal_audio_tap.h): 8 kHz, de-emphasized, squelch gated. An HR_C6000 puts it on I2S; that stream exists only while the FM feed-through is on, so the volume knob at zero mutes the amp but leaves the feed-through and the RX gain alone. The shared stream (core/usbaudio/audio_mux.c) gives APRS a ring of its own (1024 samples, 128 ms) next to a USRP session's, keeps the stream running while APRS asks for it, and drops whole 20 ms blocks when the radio task reads too late.

The path runs while the receive setting is on, the radio receives FM on the APRS frequency (aprs_cfg_freq: the region's, or the custom one) and no APRS transmission is under way. Anywhere else the stream is left alone: a repeater channel's audio isn't APRS, and battery saving would be blocked for nothing. While it runs, battery saving (radio-features.md) stays off: a sleeping receiver misses packets, and the squelch closes only after 300 ms of noise readings over the line (RADIO_SQL_CLOSE_POLLS_APRS) instead of 50 ms. A start the board's path refuses (no memory, its audio busy) is tried again 1 s later (RX_RETRY_MS), not on every tick, since each try routes the receiver and back; aprs.rx_start_fails counts them. On air, a packet's tones at full deviation pushed the AT1846S noise reading over the line for a poll or two, the squelch closed, and the squelch gated audio cut the packet in pieces: the preamble in one opening, the frame in the next. aprs_rx_listening() says whether the path runs.

The receive setting and messaging default to on when the radio can receive (aprs_can_receive(): the build has CONFIG_APRS_RX and one of the two paths exists).

The demodulator (afsk.c): 8-48 kHz samples, a correlator per tone with hysteresis and a PLL that tightens once a flag is seen. Integer arithmetic only: the float version's soft-float square roots and divisions took most of the CPU of an HR_C7000 board (no FPU) at 8 kHz.

Two decision paths run side by side on the same samples, each with its own correlators, clock and HDLC decoder: one on the audio as received, one after a high pass (two first-order stages at 700 Hz). A frame either path finds is delivered once: the second copy (same length and FCS within a quarter second) is dropped. The correlators span one bit, so their passbands are wide, and audio under the tones leaks into the mark correlator. A receiver on an HR_C7000 board getting a flat transmitter at a strong level had 320-400 Hz 1-10 dB under the tones in its receive audio and decoded 50-80 % of the frames with one path; with both, 12 of 12 (measured). The high pass alone costs weak signals (its phase shift between the tones moves the tone edges), which is why the received path stays. Both paths together take about 6 % of that CPU at 8 kHz.

Host sweep (aprsTest --sweep, frames decoded of 100, noise over the full band):

Audio 10 dB SNR 12 dB 14 dB 20 dB, 350 Hz 3 dB under the tones
Flat 96 100 100 100 (one path: 7)
De-emphasized (flat transmitter, 750 us receiver) 98 100 100 99 (one path: 57)
Pre-emphasized (pre-emphasizing transmitter, flat receiver) 11 51 76 92 (one path: 0)

Squelch gated audio loses the start of a transmission until the squelch opens; the sender's TX delay (flags before the frame) covers that. The debug link's aprs.rx_peak, rx_bits and HDLC counters tell missing audio from a demodulator that finds no frame.

A possible later step on an HR_C6000 board: its RX RAM carries the same audio ungated so packets below the squelch could be decoded. That needs a board receive path (1 above) reading the RAM 31 times a second.

6. RF transmission

The radio core's APRS block (radio_sm.c, aprs_*): when a frame waits (aprs_rf_pending) and the radio is idle (no PTT, no call heard or sent, no scan, no hotspot, no monitor, no TX inhibit, battery not critical), it takes the job (aprs_rf_take: our queued frames, 1 flag between them, a TX delay of flags before, 3 after), and:

  1. LISTEN: tunes the APRS frequency (25 kHz, or 12.5 kHz when narrow is set), 50 ms to settle, then 100 ms slots with p-persistence 64/256 while the squelch is closed; gives up after 10 s (rf_busy, retried later).
  2. KEYUP: keys the FM transmitter on the APRS frequency at APRS's own power level when one is set (aprs_power: n = the board's level n - 1), else at the radio's current level (a temporary change included, as for PTT). aprs.tx_power on the debug link is the level it will use. 25 ms.
  3. SEND: board.afsk->tx_start; at most 6 s.
  4. Back: transmitter off, the user's channel retuned exactly as it was.

PTT, a channel change or any request other than beeps, DMR focus and settings aborts it (rf_failed, the frame retried after 15 s, 3 tries). A failure counts in rf_failed and names its step in rf_fail: aborted, no_key (the transmitter didn't key), no_afsk (tx_start refused), timeout (the tones didn't finish in 6 s). The settings save waits while a job is pending.

7. Position and triggers

Position source (setting: Auto, GPS, Computer, Fixed): Auto takes a board GPS fix younger than 60 s, else a host NMEA fix younger than 60 s ($GPRMC, $GPGGA with valid checksums, from KISS SetHardware 'N' or a bare $ line on the host port), else the fixed position (settings, or the debug link). GPS and host fixes carry speed and course.

Triggers (aprs_beacon.c), all under the hard minimum:

Trigger Default
Interval 10 min when beacons are on 0 = none
SmartBeaconing on slow 5 km/h -> 30 min, fast 90 km/h -> 3 min, linear in between; corner pegging: a turn sharper than 28 degrees + 400 / speed sends after 30 s
Distance off after moving N meters
After PTT off after our own transmission (the user's PTT, not a DMR data burst list), if the last beacon is 2 min old
Manual "Send position now" (UI, debug link)
Boot off the first position after power-up

Without a known speed SmartBeaconing falls back to the interval. The defaults are an open decision (section 14).

7.1 The board GPS's state and satellites

aprs_gps() gives the UI the board GPS as it is now: false while Settings > Location > GPS is off or no valid sentence came for 60 s, else the last fix (60 s old at most), satellites used and HDOP (GGA), speed, course and time (RMC).

With CONFIG_GPS_SKY (needs GPS and APRS) nmea_sky.c also reads the board GPS's satellite sentences:

Sentence What it gives
GSV (talker GP, GL, GA, BD / GB, GQ, GN) each satellite in view: number, elevation, azimuth, SNR. A round (messages 1 to N of one talker) replaces that system's satellites; one the round no longer lists is dropped. A receiver that sends a round per signal (the NMEA 4.1 signal ID, e.g. L1 and L5) has only the first signal heard kept per system. A GN talker places a satellite by its number (GLONASS 65-96, QZSS 193-200, BeiDou 201-263 and 401-463, Galileo 301-336); SBAS and unknown numbers are skipped
GSA the fix mode (1 none, 2 2D, 3 3D) and the satellites the fix uses. The GSAs in a row (one per system, or several GN) make one round; the first clears the used set. A GN GSA takes its system from the NMEA 4.1 system ID when present, else from the numbers

The table holds 28 satellites of 4 bytes each (number, elevation, system, SNR, azimuth / 2); the used sets are a 64-bit mask per system. In all 168 B, kept in the APRS state under its lock; aprs_gps_sat(i) copies one satellite out, so a reader needs no table of its own. Turning the GPS setting off empties it.

8. Positions over DMR

Send via (AFSK / DMR / Both): DMR positions go out when the radio is on a DMR channel with the call stack running, as a private data call to the destination ID (default 310999). BrandMeister's APRS gateway is a private ID "MCC999", with MCC replaced by the master's MCC (310999 for US masters, 204999 for a Dutch one). 310999 works from any master; your own master's prefix (<MCC>999) is recommended. The old 5057 was retired on 2019-03-01 (https://news.brandmeister.network/gps-5057-destination/, https://wiki.brandmeister.network/index.php/APRS). A radio that already saved its APRS settings keeps the stored value: run aprs set dmr_dst <MCC>999 (US: aprs set dmr_dst 310999).

  • Motorola LRRP triggered location report (the default): UDP port 4001, built by dmrLrrp.c.
  • ETSI UDT location (dmr_udt on): dmrDataBuildUDTLocation. BrandMeister forwards the position reports of Motorola (LRRP) and Hytera radios to APRS, so use LRRP, not UDT, for BrandMeister.

8.1 LRRP details

An unsolicited report carries request ID 00000001 (no request came). BrandMeister's gateway accepts it with no ARS registration. Checked on master 3102 through a hotspot: each report to 310999 appeared on APRS-IS as <callsign>-7>APBM1D,<callsign>,DMR*,qAR,<callsign>:=<lat>/<lon>[<name> D (SSID and comment from the BrandMeister profile, tocall APBM1D). The retired 5050-5059 IDs chose the SSID and icon; whether MCC999 does is unverified (HIL step, section 13). The APRS settings on BrandMeister's self care page set the comment.

8.2 Positions heard over DMR

Some radios of other makes send their DMR APRS position as an NMEA sentence over UDP port 10110 (compressed UDP/IP header, SAP 3), as a private data call or to a talkgroup, usually to the APRS gateway's ID (BrandMeister's 999, e.g. 310999). As on FM, where every station heard goes into the list, the data service hands each one heard on the channel to the radio core whoever it is addressed to (only control station passthrough, our own repeated back and a compressed header for a host behind the radio, DAID 1, are left out; dmr-data.md 3; the stock radio sends DAID 0). The core parses it with nmea_parse_unchecked and puts a position with a fix into the station list through aprs_dmr_heard, as a caller's GPS Info. The packet has no callsign and a data call no talker alias, so the station is named by the DMR ID database (dmr_iddb_lookup: its callsign, the record's first word) when the ID is in it, else by the DMR ID. BrandMeister names the same report on APRS-IS by the callsign registered for the ID, with the SSID set in SelfCare; no SSID is made up here. What one stock radio sent on air: $GPRMC,...,3000.0000,N,8700.0000\0,W,...,A with no *hh checksum, a NUL before ,W, a NUL from the pad after the sentence, and the longitude with 4 integer digits. The checksum is optional here (the packet's CRC-32 covered it, but one that is given is checked), NULs are skipped, and the coordinates are read as numbers, so 8700.0000 is 87 degrees (anything past 90 / 180 degrees, or an integer part too long to scale, is refused, as in nmea_parse). Time and date are not used. Like a caller's GPS Info, it does not depend on the APRS receive setting (rx_on, which is the FM decoder's). Host tests: aprsTest (test_nmea_unchecked: input classes, the 90 / 180 degree, 60 minute, 32-bit and length edges, a seeded fuzz and a metamorphic check against the strict parse), dmrDataServiceTest (testNmeaPosition: the packet heard on air rebuilt with placeholder IDs, its blocks and CRC-32 matching the ones heard; the packet classes, ports and payload lengths) and radioCoreTestAprs (test_dmr_aprs_position: through the radio core into the station list).

9. Host port: KISS TNC

With Tools > APRS > KISS on USB, the host link claims KISS frames (0xC0) and $ lines on the USB serial port (and on UDP port 3334 in USB network mode, where the host port runs over the network). The host link's TNC mode (HOSTLINK_TNC) holds while frames flow and ends after 30 s idle, like the hotspot and CPS modes.

Port 0 only. DATA frames are checked (section 3) and queued for RF; TXDELAY, PERSIST and SLOTTIME are accepted and ignored (the engine's own CSMA applies); SetHardware 'G' carries an APRS-IS line, 'N' an NMEA sentence. To the host: received frames (RX builds) as KISS DATA, and lines for APRS-IS as SetHardware 'G'.

9.1 iGate

The rules are in firmware (aprs_igate.c, after aprs-is.net "IGate Design" and "q Construct"); the APRS-IS TCP connection is in the host helper tools/aprs_gate.py, which logs in with the callsign and passcode, applies a filter, and passes lines both ways as KISS SetHardware 'G' frames (serial port or UDP 3334), or, in USB network mode, in the radio's own APRS-IS client (CONFIG_NET_APRSIS, tcp-services.md 7), which carries the same lines.

  • RF -> IS: never a packet whose path holds TCPIP, TCPXX, NOGATE or RFONLY, nor a query; a third-party packet is gated as the packet it carries unless that one came from the Internet; the line gets ,qAR,<our call>; each packet once per 30 s.
  • IS -> RF (igate_tx, off): only messages and acks, to a station heard directly in the last 30 minutes, from a station not heard on RF in the last 10 minutes, not seen in the last 30 s; sent as }SRC>DST,TCPIP,OURCALL*:info.
  • Our own beacons to APRS-IS (to_is): CALL>APZRDF,TCPIP*:info over the same channel.
  • Digipeater (RX builds, off): fill-in (WIDE1-1 only) or wide (WIDE1-1 and WIDE2-N), also our own call in the path; our call inserted before the WIDE entry, marked used (traced), the hop count decremented; duplicates suppressed for 30 s.

10. UI

Tools > APRS (ui_aprs.c): the body shows the callsign and frequency, why nothing can be sent (no callsign, no frequency, not amateur, no AFSK), the position and its source, the next beacon and the count sent. Rows: Send position now, Automatic beacons, Frequency (region: N America 144.390, Europe 144.800, Australia 145.175, New Zealand 144.575, Japan 144.660, China 144.640, Custom), Power (As channel, then each of the board's levels), Path (the digipeater path of RF packets: None, WIDE1-1, WIDE1-1,WIDE2-1, WIDE2-1, WIDE2-2; OK steps to the next and WIDE2-2 wraps to None), Interval (Off, 1, 2, 5, 10, 30 min), SmartBeaconing, Send via (AFSK, DMR, Both), Position (opens its page, below), Position in DMR calls (dmr-voice.md 3.1), KISS on USB, Stations heard (RX builds: the list, most recent first, dimmed when heard through a digipeater; a station with a position shows its distance and compass point from ours, aprs_position_now as DMR GPS Info uses it, "14.5 km NE" by sb_range, left out first when the row is too narrow, and nothing without a position on either side; that row's age is the position's, pos_ms, else the last packet's. An object's or item's position is not stored as its sender's; a station's position is dropped at 24 h, STATION_POS_MS, so its age never wraps). On a radio without board.afsk, Power and Path are dimmed, and so is Frequency unless the build receives APRS. Rows 11 and 12 have no digit key.

APRS > Position (aprs-pos; the APRS page's body shows the position in use). Rows: Source (Auto, GPS, Computer, Fixed), Latitude and Longitude (the fixed position, "Not set" while both are 0). OK on Latitude or Longitude opens an editor in decimal degrees to 4 places (about 11 m): the digits fill DD.DDDD or DDD.DDDD from the left (a longitude's first digit of 2-9 gets its leading zero by itself), a digit that would pass 90 or 180 degrees is refused with a toast, Up / Down or the knob switch N / S or E / W, BACK takes the last digit back (with none typed it closes the editor unchanged), OK saves with the places not typed as zeros. The editor starts with the stored value shown; the first digit starts over. Find knows the page as "APRS position".

The rest (symbol, comment, digital destination, ambiguity, iGate, digipeater, fixed altitude) is set over the debug link (rfoundry_debug.py aprs set) and later by qdmr through the native codeplug.

11. Settings

settings_t (core/settings/settings.h), taken only when aprs_stored is 1, else the defaults: aprs_flags (TX, SmartBeaconing, compressed, after PTT, boot, KISS, narrow, RX), aprs_flags2 (iGate, iGate TX, messaging, UDT, to APRS-IS, DMR GPS Info), aprs_region, aprs_ssid, aprs_freq_hz, aprs_lat / aprs_lon (1e-7 degree), aprs_dmr_dst, aprs_alt_m, aprs_interval_s, aprs_distance_m, aprs_symbol, aprs_path, aprs_pos_src, aprs_level, aprs_power (0 = the radio's current level, n = the board's level n - 1), aprs_txdelay, aprs_misc (via, digipeater, ambiguity). The codeplug's APRS record 1 (EEPROM 0x1588, 64 bytes each) gives the SSID, path, symbol, comment and ambiguity when present; its fixed position and frequency encodings are unknown and not used. Of these the radio takes the comment and the path.

The path lives in both places, and the one changed last wins:

  • Codeplug to radio: when the record's path differs from aprs_cp_path (the codeplug path last taken, aprs_path_t + 1), it replaces aprs_path and becomes the new aprs_cp_path. Checked at start-up and whenever the codeplug serial changes (a CPS write). A path that isn't one of the five (another callsign, a second entry without a first) is left alone.
  • Radio to codeplug: a change on the APRS page posts a storage job (aprs_path_to_codeplug) that rewrites the record's two path entries (callsign space padded, SSID byte) and sets aprs_cp_path to match. No record is made when there is none: the radio then keeps the path in the settings only. A change over the debug link stays in the settings.

Defaults: TX off, region N America, SSID 7, symbol /[, path WIDE1-1,WIDE2-1, compressed, interval 10 min, SmartBeaconing on, level 50, TX delay 300 ms, via Radio, destination 310999; messaging and RX only on boards with a receive path.

12. Size

The flash and RAM cost of CONFIG_APRS and CONFIG_APRS_RX per radio is in feature-matrix.md. CONFIG_DMR_GPS costs about 2.2 KB flash.

13. Tests

Host: make -C tests/host runs aprsTest (spec vectors: AX.25, FCS, stuffing, KISS, every APRS format, NMEA, the triggers, the iGate rules, a modulate / demodulate loop; --sweep prints the decode rate against SNR, --wav DIR writes aprs_test_44k1.wav with three frames) and aprsRadioTest (the engine with the radio core: beacons on the air decoded back, the minimum interval, CSMA on a busy channel, PTT cutting a transmission, the not-amateur block, manual and after-PTT triggers, KISS and NMEA from the host, messages with acks, the station list, digipeating and the iGate both ways, the DMR position source, a packet as AFSK audio through the PCM stream into the station list only on the APRS frequency). usrpNetTest checks the stream's APRS ring. tests/host/aprs_wav_check.sh feeds the WAV to Direwolf's atest when it is installed (a black-box check).

HIL (opt-in, tests/hil/test_aprs.py, README there): --allow-rf --allow-service-stop --aprs-call CALL (a licensed operator's callsign, set as the RAM-only test call; SSID --aprs-ssid, default 15).

Where the AFSK tests transmit, so nothing reaches the APRS network: the custom region on --aprs-test-mhz (a simplex frequency the operator may use, away from the APRS network: 144.390 would be digipeated and gated to APRS-IS, and every regional APRS frequency is refused), no path, the radio's lowest power (checked through aprs.tx_power before keying; 6 RF says APRS keys at the radio's current level). Each transmission is about 0.6 s. Every APRS setting touched is saved and put back once the queued frames and message retries have gone out on the test frequency.

  1. Position, status, message: a bench board decodes Bell 202 / AX.25 itself while the radio sends (an FM receive job with APRS decode: frames with a good FCS and their tone measurements, no I/Q). Asserted: a valid FCS, CALL-SSID>APZRDF with no digipeaters, the payload (position within 1e-4 degree, >status, :ADDRESSEE:text{N), aprs.rf_sent +1, the radio back on its channel, deviation peak 2.0-4.5 kHz, twist under 6 dB. Printed: mark and space deviation, TX delay (flags before the frame). The position is sent from the DMR channel and from the FM VFO.
  2. Digital: on the DMR test channel the position goes to --aprs-dmr-dst as an LRRP report and as a UDT location; the bench modem's listener hears the header and blocks, the test checks the CRCs, the IPv4 / UDP 4001 / LRRP or UDT NMEA layers and the position (within 2e-5 degree). Passed on air (a 70 cm DMR channel, color code 2, 50 mW).
  3. Still manual: BrandMeister (aprs set via dmr through a BrandMeister repeater or hotspot; the position on aprs.fi with the SSID BrandMeister assigns, and the LRRP request ID question, 8.1) and KISS (aprs set kiss 1, tools/aprs_gate.py --port PORT --kiss-tcp 8001, or in USB network mode the radio's own KISS port 8001; a position from APRSdroid / Xastir goes out on RF).

Results (SDR bench): DMR LRRP and UDT pass. AFSK decodes off air with a valid FCS on each kind of board with a tone path, with twist under 1.5 dB and continuous phase (about 6 to 15 degrees residual); the tone level is set per board so the peak deviation stays near 3 kHz (a transmitter limits at 5.1 kHz peak). test_aprs.py on the 2 m amateur band is still to run on the HR_C7000 boards. Per-radio levels and results are in the hardware specs.

14. Open decisions

  • Default triggers: SmartBeaconing on with a 10 min interval fallback, after-PTT and boot off.
  • A registered tocall instead of APZRDF.
  • The LRRP request ID and token for unsolicited reports to BrandMeister; whether LRRP answers should use the APRS position when there is no GPS; whether Settings > Location > Send position should drive APRS.
  • The codeplug APRS record's fixed position and frequency encodings.
  • Receive from the HR_C6000's ungated RX RAM (5.3).
  • Settings and debug link section / command numbers may need renumbering when merged with other branches.

Future, not done:

  • 9600 baud APRS (G3RUH FSK, scrambled NRZI direct FM): needs a flat (discriminator / modulator direct) audio path and an FSK modem; a tone generator path can't do it. Not planned for now.
  • Settings > Location tied to APRS; GPS Info in the hotspot's own transmissions.

Ver el código fuente de esta página