Repository navigation
Releases
pdn_aprs 1.0.1
Refusals carry a reason code.
- A refusal now says why in a form a program can act on:
pdn_aprs_encodedgainsint refusalat the end (apdn_aprs_refusalcode, zero for none and for every error that is notPDN_APRS_ERR_REFUSED), andpdn_aprs_refusal_name()gives the vectors' kebab-case id. The 26 codes are the refusal ids of the shared vectors; new ones may be appended in a later 1.x release, so keep a default case. Build calls fill it inresulttoo. The message text is unchanged and still not stable API. - Fixed: a header address the encoder cannot write (
pdn_aprs_encode_tnc2andpdn_aprs_encode_ax25) returnedPDN_APRS_ERR_ARGUMENT, though the header documentsPDN_APRS_ERR_REFUSED. It is nowPDN_APRS_ERR_REFUSEDwithPDN_APRS_REFUSAL_INVALID_ADDRESS(TNC2) orPDN_APRS_REFUSAL_AX25_CANNOT_CARRY(AX.25) and a reason. NULL pointers stayPDN_APRS_ERR_ARGUMENT. - An altitude given to
pdn_aprs_build_mic_ein feet and in metres is nowPDN_APRS_ERR_REFUSEDwithPDN_APRS_REFUSAL_VALUE_GIVEN_TWICE, where it wasPDN_APRS_ERR_ARGUMENT. - When data breaks several rules the refusal names the first by the shared order of the vectors' DESIGN.md (a fault in a field on its own before one about how fields relate; the header before the data).
- An address written with SSID
-0(N0ZO-0) is now refused bypdn_aprs_encode_ax25()andpdn_aprs_write_ax25()withPDN_APRS_REFUSAL_AX25_CANNOT_CARRY, likeWB4APR-00, since a frame gives it back asN0ZO. This holds for a third-party inner header too. It was written with no SSID. - The dump tool writes
refusal(encode and build),reencode_refusal(decode) andax25_refusal(the API view) in its records.
pdn_aprs 1.0.0
This is the first stable release. The public API is now frozen under semantic versioning. From 1.0 the major and minor version are shared with aprs-vectors and the other four libraries: a ruling round is a minor version for all five, this library's own fixes are patch versions, and an API break is a major version.
The 1.0 API cleanup: one consistent shape for every build call, one way to report a refusal, and a written compatibility promise. Breaking changes are listed first, with what to do about each.
Breaking changes
What 1.x promises, which is new: source compatibility only. Struct sizes follow the capacities and a 1.x release may add a field to any struct, so rebuild everything that includes pdn_aprs.h on upgrade, link the static library or compile in the single file, and zero-initialise every input struct (a field added later means "absent" when zero).
- The CMake library target is always STATIC (
BUILD_SHARED_LIBSis ignored) and no longer has a SOVERSION. It is built with hidden visibility, and the public functions are markedPDN_APRS_API. pdn_aprs_decode_options.rejectis now a fixedreject[PDN_APRS_MAX_CODES](128) instead ofreject[PDN_APRS_CODE_COUNT], so a new code never changes the struct. Designated initializers (.reject = {[PDN_APRS_CODE_X] = 1}) are unaffected.- Every
pdn_aprs_build_*call, andpdn_aprs_build(), gains a trailingpdn_aprs_encoded *result(may be NULL): it getslen, the Mic-Edestinationand, on failure,reason(plain text, not part of the stable API). Add, NULLto keep today's behaviour. - One input struct for every build call with optional inputs, with
name,timestamp,killed,messaginginside the struct that uses them:pdn_aprs_positionis nowpdn_aprs_position_argsand gainsname,killed,timestampandmessaging.pdn_aprs_build_position(station, &args, buf, cap, result),pdn_aprs_build_object(station, &args, buf, cap, result)andpdn_aprs_build_item(station, &args, buf, cap, result)lose theirtimestamp,messaging,nameandkilledparameters.pdn_aprs_mic_eis nowpdn_aprs_mic_e_args; itsmessageis nowmic_e_message.pdn_aprs_status_optionsis nowpdn_aprs_status_args, andpdn_aprs_build_status_options()is nowpdn_aprs_build_status(station, &args, buf, cap, result). The old text-and-timestamppdn_aprs_build_status()is gone (set.textand.timestamp).- New
pdn_aprs_message_args { addressee, text, message_id, reply_ack }:pdn_aprs_build_message_reply_ack()is nowpdn_aprs_build_message(station, &args, buf, cap, result). The old four-stringpdn_aprs_build_message()is gone (leavereply_ackNULL). - New
pdn_aprs_telemetry_args { sequence, analog[5], empty[5], bits, comment }:pdn_aprs_build_telemetry_channels()is nowpdn_aprs_build_telemetry(station, &args, buf, cap, result).empty[i]set leaves channel i empty (zero means present, so a zeroed struct has all five); the old five-valuepdn_aprs_build_telemetry()is gone.bitsNULL still gives00000000.
- Builder inputs now match the recipe: the telemetry
sequenceis anunsignednumber 0-999 (refused above, written with three digits), where it was text; the bulletin takeschar idandconst char *group(NULL for none) where it took one string like"4WX"; Mic-E takes amessagingflag, andtype_codeis only for a device type ('>',']'or' '), sotype_code = ''or'''is now refused and a Mic-E report is always written with a type code; telemetry metadata calls (_names,_units,_coefficients,_bits`) accept a NULL addressee, meaning the station itself. - Builder error codes now depend on the input, not on the C buffer:
PDN_APRS_ERR_REFUSEDfor anything APRS cannot carry (a name that is too long, a bad station address),PDN_APRS_ERR_ARGUMENTonly for a NULL required pointer or other misuse (altitude in feet and metres both), and the newPDN_APRS_ERR_UNSUPPORTED(-6) for text APRS allows that does not fit the fixed buffers (over 1,024 bytes). A switch over the result codes needs a case for -6. - Weather indexes carry their units, and the enum has a type name,
pdn_aprs_weather_field:PDN_APRS_WX_WIND_DIRECTIONisPDN_APRS_WX_WIND_DIRECTION_DEGREES,_WIND_SPEED_WIND_SPEED_MPH,_WIND_GUST_WIND_GUST_MPH,_TEMPERATURE_TEMPERATURE_F,_RAIN_1H_RAIN_1H_IN,_RAIN_24H_RAIN_24H_IN,_RAIN_MIDNIGHT_RAIN_MIDNIGHT_IN,_HUMIDITY_HUMIDITY_PERCENT,_PRESSURE_PRESSURE_MBAR,_LUMINOSITY_LUMINOSITY_W_M2,_SNOW_24H_SNOW_24H_IN.PDN_APRS_WX_RAIN_RAWis unchanged. - The
#definegroups are now named enums (the fields stayuint8_t):pdn_aprs_fix,pdn_aprs_source,pdn_aprs_origin,pdn_aprs_area_shape,pdn_aprs_storm_type,pdn_aprs_dao_precision,pdn_aprs_tone,pdn_aprs_raw_weather_format.PDN_APRS_NMEA_OTHER,_GLL,_GGAand_RMC(the compression source) are nowPDN_APRS_SOURCE_*. - Renames:
pdn_aprs_status.beam_headingandbeam_powerarebeam_heading_codeandbeam_power_code(and the same inpdn_aprs_status_args);pdn_aprs_reasonispdn_aprs_unrecognized_reasonandPDN_APRS_REASON_*isPDN_APRS_UNRECOGNIZED_*;pdn_aprs_meta_item()ispdn_aprs_telemetry_meta_item();pdn_aprs_decode_written()ispdn_aprs_decode_info_unbounded(). pdn_aprs_options_strict()is removed: writepdn_aprs_decode_options o = {.strict = 1};or seto.strict = 1.pdn_aprs_capabilities.itemis an array of the named structpdn_aprs_capability(it was an anonymous struct); same fields.pdn_aprs_ack.idkeeps its name (the neutral form calls itacked_idorrejected_id, andmessage_idwould mean the ack's own ID elsewhere in the family).pdn_aprs_reportgainsaltitude_in_comment(see Mic-E below), so struct layouts change.
Added
pdn_aprs_format_timestamp(year, month, day, hour, minute, second, format, out)writes a UTC time asDDHHMMz,HHMMSShorMMDDHHMMfor the builders, cutting to the format's step.pdn_aprs_write_tnc2()andpdn_aprs_write_ax25()write a decoded packet back as received (header and information field), for an IGate or digipeater. They returnPDN_APRS_ERR_HEADERfor an unusable header andPDN_APRS_ERR_REFUSEDfor an address AX.25 cannot carry as sent.pdn_aprs_phg_values()gives a PHG's watts, height in feet, gain in dB and directivity in degrees (-1 for omnidirectional).pdn_aprs_has_diagnostic()takes-1for any code, sopdn_aprs_has_diagnostic(&p, -1, PDN_APRS_SEVERITY_ERROR)is has-errors and..._WARNINGhas-warnings.PDN_APRS_COLOR_*constants (the area colours, which were documented but missing), apdn_aprs_colorenum, andpdn_aprs_capability_token()andpdn_aprs_capability_value().- Mic-E: the decoder sets
altitude_in_commenton a report whose altitude was sent as a/A=, and the encoder writes it that way, so data read from a packet writes the bytes it was read from. The builder sets it for an altitude in feet. It is outside the vectors' neutral form. - The diff dump tool's API view gains
has_errors,has_warnings,tnc2,ax25andphg.
pdn_aprs 0.3.0
API changes:
- New builder calls, so nothing existing changes:
pdn_aprs_build_telemetry_channels()(a presence flag per analog channel, so a channel can be empty;pdn_aprs_build_telemetry()calls it with all present),pdn_aprs_build_status_options()withpdn_aprs_status_options(locator with symbol, and beam heading and power;pdn_aprs_build_status()calls it), andpdn_aprs_build_message_reply_ack()(reply-ack,""for support with nothing to ack). Because the public structs change as below, this still needs a minor version bump. pdn_aprs_third_party.packetnow holds2 * PDN_APRS_MAX_INFObytes, as text does, so the encoder takes an inner packet longer than the decoder reads off the air; onlypdn_aprs_third_partychanges layout (pdn_aprs_dataandpdn_aprs_packetkeep their sizes and offsets), but rebuild anything that includespdn_aprs.h.pdn_aprs_decode_third_party()decodes such a long inner packet, which keeps no copy of its information field, and returnsPDN_APRS_ERR_ARGUMENTfor alenlarger thanpacket.- The
pdn_aprs_build_mic_e()locator is now sent in upper case, as the status builder's is. - The diff dump tool refuses (no longer reports
unsupported) a third-party packet whose inner data is unrecognized but not empty. - Deliberate design limits, now written in the README: text longer than 1,024 bytes and text holding a NUL are not buildable through the builder (no allocation, C strings); the diff dump tool reports them as
unsupportedand nothing else. - New
pdn_aprs_decode_written(): decodes an information field aspdn_aprs_decode_info()does, but keeps no copy of it, so it takes one longer thanPDN_APRS_MAX_INFO. It returnsPDN_APRS_ERR_TOO_LONGonly when a part of the field is longer than the packet's data holds. pdn_aprs_querygainslatitude_textandlongitude_text, a general query's footprint numbers as sent, so struct layouts change: rebuild everything that includespdn_aprs.h.course_degreesinpdn_aprs_report,pdn_aprs_positionandpdn_aprs_mic_e, andradius_milesinpdn_aprs_query, are nowdouble, so a program can give a course or a radius between whole numbers and the encoder rounds it to the step of the field it goes in (a compressed course to 4 degrees, once). Decoded values are still whole numbers. Code that prints them with%uneeds a cast or%.0f.pdn_aprs_mic_egainshas_altitude_mandaltitude_m, an altitude in metres for the Mic-E altitude bytes, rounded to the metre.altitude_feetis now always written as a/A=(vectors ruling B2); before, the encoder put it in the Mic-E bytes whenever it was a whole number of metres. Give one or the other.pdn_aprs_build_message()refuses an empty message ID (""), which it took as none; pass NULL for none (ruling B8).
Encoding:
- No size limit (vectors ruling E11: the rules set none). The encoder refused an information field over 512 bytes, which text received as Latin-1 and written back as UTF-8 can be; it now writes any length that fits the buffer, straight into it, and checks what it wrote with
pdn_aprs_decode_written().PDN_APRS_ERR_BUFFERmeans the buffer is too small to write and check the field. - Exact bytes (vectors rulings E1-E12): the vectors'
canonical_infois now binding, so where the spec allows several forms the encoder writes the one the Encoding rule names.- A general query's footprint numbers are written as sent, with or without a leading space (
?APRS? 34.0,-117.15,0200,-.1715); it wrote them normalised (E1). - After the data extension a position, object or item report's comment is written in the order the voice frequency with its fields, the braces, the
/A=altitude, the free text after a space, base-91 telemetry, the!DAO!(j006/058/146.520MHz/A=000889 Dayton Bound): the frequency comes first, in the bytes radios read (APRS12c ch. 18); it came after the altitude (E3). - A Mic-E speed of 190-199 knots is written with
/, the printable one of its two forms; it wrote DEL (E5). - Mic-E status text starting with 0x1D after Rev 0 telemetry is written straight on; the
/is only for status text that would start with 0x1D (E6). - No delimiter where none is needed: none before
.../...text after a compressed position, which has no data extension to mistake it for, and none before text after a Mic-E type code that only looks like a locator (wO91SX/G: a locator's first two letters are A-R). The space after a frequency is written before a comment that starts with a NUL, which was taken for a tone letter (E7). - Snowfall under 1 inch is
.and two digits (0.5 as.50); it wrote0.5(E8). - A compressed wind direction that rounds to 360 degrees is written as north (c = 0); a 358-degree wind was refused (E9). A compressed course that rounds to north (1 degree, say) is no longer refused either.
- A
!DAO!on a compressed position carries the digits of the position the compressed bytes report; they were taken from the position given, which can differ by a fraction of a compressed step (E2).
- A general query's footprint numbers are written as sent, with or without a leading space (
- A value is range-checked once rounded to the step its field holds: a humidity of 99.7% is written
h00(100%), a speed of 999.4 knots999, a luminosity of 999.7 W/m2l000. They were refused. - NMEA data whose position, fix, course, speed, altitude, time or waypoint disagrees with its sentence is refused, since the sentence is written as it is and would read back with its own; the fields were not checked. Fields left unset are not checked, so data holding only a sentence is still written.
- Exact bytes, batch 2 (vectors rulings F1-F17, B1-B8, G1-G3 and the run 2 rulings; vectors e27a79f):
- Values between steps are rounded once to the nearest step, halves away from zero, a value within 1e-6 of a half (measured in steps) being the half, and checked after rounding (F2): a course or speed,
/A=feet, Mic-E speed and course, latitude and longitude minutes, DAO digits, weather values, RNG miles, a footprint radius, a voice frequency, and what the cs bytes carry. Negative halves went toward +infinity (-0.5 wast000and/A=000000; nowt-01and/A=-00001), and halves were decided without the tolerance (58.584999999999996 minutes from 0.9764166666666666 degrees is now58.59). A course of -0.3 is 0 and a course of 360.4 is 360. A value the cs bytes carry rounds to the nearest step of their formula for any whole s, and is refused when that s is outside the byte (G3): a compressed speed of -0.03 knots is written as 0, -0.3 is refused, a range of 1.95 miles is written as 2. With a!DAO!the position is taken in DAO steps, rounded once, and split into the main digits and the DAO digit. - A course of 0 is written only where it reads back as 0:
000uncompressed, refused in Mic-E and compressed reports, after rounding (F1). A compressed course needs a speed and a speed a course (F5). - Snowfall that rounds to a whole number is three digits (0.995 is
001, 9.96 is010), and one that rounds past 999 is refused (F3). - A frequency after a DF bearing or storm data follows a
/(F9). - Numbers given without their text (F11): a telemetry value that is a whole number 0-999 is three digits (
005,000,091); a general query's footprint latitude and longitude that are not negative take a leading space, 0 included (F15), and the radius is rounded to whole miles. - A compressed weather report's GGA altitude goes in the cs bytes alone, rounded to their step, as it has no comment for a
/A=(F14); it was refused. - Comment text holding a PHG, RNG or DFS after an area is refused, since it now reads back as the data extension (F7a). A Mic-E locator followed straight by base-91 telemetry or a
!DAO!is written (F16). - Text after an area symbol that only looks like an area (
81019310, whose1form would give colour 19) is written straight on, as a decoder does not read it as one; it was written after a/(E7). - A luminosity that rounds to 1000 W/m2 (999.5) is
l000; it was refused. - A Mic-E comment starting with
/that a/delimiter would still join into an altitude (/{}after a/A=) is written after a space; it was refused. - The ambiguity cut takes the 1e-6 tolerance in the box's units, so 80.49 degrees (29.40 minutes) is the box 29.4x (G2).
- With ambiguity, the Mic-E longitude digits a decoder ignores hold the centre it reports (README Mic-E rule); they held the position's own digits.
- Where neither the canonical form nor a
/reads back, a space is written before the comment: a Mic-E comment such as{5}after a/A=would join its digits into a Mic-E altitude, and/is a base-91 byte itself (README Encoding rule, E12). - The builder passes a compressed course on as given, so it is rounded once: 301.7 degrees is
l, where whole degrees first gavem. A Mic-E builder course that rounds to 0 is the spec's unknown (B1).
- Values between steps are rounded once to the nearest step, halves away from zero, a value within 1e-6 of a half (measured in steps) being the half, and checked after rounding (F2): a course or speed,
Decoding:
- A PHG, RNG or DFS later in an area object's comment is found as the data extension, as after a course/speed (ruling F7a: an area, like a DF bearing or storm data, describes the object, not the station's radio).
- A later Mic-E altitude is looked for before base-91 telemetry and a
!DAO!are lifted from the status text (only the locator is read as if they were out), so bytes either side of a!DAO!never join into an altitude, and a!DAO!that shares a byte with the altitude found is not one ("13.7V!w*@!7e}: the altitude is!7e}). Withmic-e-altitude-not-firstswitched off, a report with a later altitude is rejected when the text left, once telemetry and the!DAO!are lifted, holds an altitude formed across a place one was taken from (an altitude the bytes already held is kept); with no later altitude, a strict decoder reads the status text as a lenient one does. - A PHG or RNG straight after a Mic-E locator and its space is lifted before a later Mic-E altitude is looked for, so its digits are never one (G1; this already ...
pdn_aprs 0.2.0
Brings the library into line with the rulings from differential fuzzing of all five implementations (packet-net/aprs-vectors, "Rulings from differential fuzzing: 120 cases and the rules behind them"). The vectors move to dc3854a, which adds rounds 2 to 7 of the fuzzing rulings and settles signpost and corridor braces.
API changes. Struct layouts change, so rebuild everything that includes pdn_aprs.h:
pdn_aprs_nmeagainscommentandcomment_len: the text after a sentence's checksum, as sent.pdn_aprs_nmea.timeandwaypointare nowPDN_APRS_MAX_INFObytes. The fraction of a second is kept as sent whatever its length (it was cut at 6 digits), and a WPL waypoint name is no longer cut at 19 characters.sentenceis nowPDN_APRS_MAX_INFObytes as well, since it is always printable ASCII.pdn_aprs_telemetry.sequenceis nowPDN_APRS_MAX_INFObytes. A sequence of 20 or more characters used to make the report invalid.query_typeinpdn_aprs_directed_queryandpdn_aprs_queryis nowPDN_APRS_MAX_INFObytes, so a query type of any length is read.pdn_aprs_number.textis left empty when the number as sent is longer than it holds, and the value is still read. A telemetry value of 24 or more characters used to make the report invalid.pdn_aprs_packetgrows from about 2.6 KB to 3.4 KB, andpdn_aprs_datafrom 1.8 KB to 2.6 KB.
Decoding:
- NMEA. The text after
$must be an NMEA 0183 sentence, or it isinvalid-nmea: printable ASCII, an address of five upper-case letters or digits (orPand three or more), at least one field, and no$or stray*. This is checked before the checksum. A sentence ends at its first*and two hex digits, the checksum is checked, and any text after it is the newcomment(TinyTrack's/Home Station). A position needs both coordinates, each with a degree digit, minutes under 60 and a value in range. A time is exactlyhhmmsswith valid hours, minutes and seconds. GGA's fix is one digit. A proprietary sentence ($PGRMC) is kept as text, never read as RMC. - Mic-E. The 0x1C and 0x1D data type identifiers get an
obsolete-formatinfo, whatever follows them. Kenwood 0xFF padding is removed only from the status text, once the destination and the nine fixed bytes have decoded: an 0xFF among the fixed bytes is out of range, and a report that fails earlier has no padding warning. - Weather. In a position, object or item report the wind is decided first (sent as
candsfields, or missing), before any field's width. A secondsafter the wind speed is snowfall.Landlare one field. A wind direction over 360, in acfield or in an extension after a compressed position, isout-of-range-valueand dropped. An extra field's value is the whole run, and it must end in a digit. - Positions. An ambiguous latitude whose reported centre is past the pole (
90 . N, centred on 90 degrees 30 minutes) isinvalid-latitude, and a Mic-E destination like it isinvalid-mic-e-destination; a longitude whose centre is past 180 degrees (180 . Wunder that ambiguity) isinvalid-longitude. A DF report's bearing over 360 isout-of-range-value, and the whole/BRG/NRQis dropped.!DAO!precision added to a zero latitude or longitude in the south or west (0 degrees 0 minutes S) goes south or west too; it went north or east. The latitude alone sets the ambiguity: the longitude may hold digits or spaces in the places the latitude blanks, and a space anywhere else isinvalid-longitude. A garbled timestamp, in an object or a/or@report, is judged on the position straight after it, not on the rest of the report. A digit!DAO!datum is read when A and O are spaces. A signpost or corridor is the first well-formed braces wherever they are:{, 1-3 characters that are not braces, and}, the characters printable ASCII for a signpost or digits for a corridor. Braces that do not qualify are comment text and do not stop the search, so{5<0x01>5} {AB}has the signpostABand a line object's{AB}{5}a 5-mile corridor. In a compressed position the latitude and longitude, and their range, are checked before the symbol code. Base-91 telemetry's binary value is the eight channels, 0-255. - Messages. A bulletin is
BLNthen a digit or an upper-case letter. Bulletins, NWS bulletins and telemetry metadata do not take the reply-ack form:{MM}AAon them isbrace-in-message-text, and the text is kept. A stray{in aPARM.,UNIT.orBITS.list stays in the list, which is still telemetry metadata. Only a message can be a directed query or telemetry metadata: bulletin and NWS bulletin text starting with?,PARM.,UNIT.,EQNS.orBITS.is text, with noinvalid-queryorinvalid-telemetry-metadatainfo (APRS12c ch. 15, queries are "addressed to individual stations"; ch. 13, the metadata addressee is "the callsign of the station transmitting the telemetry data"). A directed query's target is one callsign (letters, digits or-); one space may separate it from the type, and spaces after it are padding. - Numbers. Telemetry values,
EQNS.coefficients and a general query's footprint take no+, and a footprint value's leading space is only for a positive value (APRS12c ch. 15), so a space before a minus sign isinvalid-general-query. A coefficient of0eNis 0, and one that is not a finite number (1e400) makes theEQNS.a plain message. By the same rule a telemetry value too large for a double isinvalid-telemetry, and an NMEA speed, course or altitude that large is left out. - Capabilities. Spaces around
=are padding. An empty token, or a control character in a value, makes the report free text. - Status. Spaces before a
^HPbeam heading are part of the text. - Third-party. The source inside may be any 1-9 printable ASCII characters other than
>and:, and a q-construct is read only in the outer header. A defect its header may tolerate is a warning on the inner packet, and a strict decode rejects the packet asinvalid-third-party(it gave an empty inner packet).
Encoding:
- Writes an NMEA comment after the checksum, and refuses a comment on a sentence without one.
- Writes a GGA compressed altitude under 1 foot as
!!in the cs bytes and the altitude in/A=(it refused), a directed query's target after one space for a type the spec does not define and an APRSH target padded to 9 characters, a digit!DAO!datum, snowfall in a positionless report, a snowfall under 1 inch in hundredths (0.32 as.32; it refused), status text exactly as it is before^HP, a negative general query latitude with no space before it (it wrote one), and numbers of 1.8e19 and over (it refused them). - Writes Mic-E status text that starts with 0x1D after a
/(APRS12c ch. 10: status text must not start with 0x1D); it wrote it straight after the fixed bytes whenever that happened to read back. - Refuses a DF bearing over 360, a capability value that holds a control character or starts or ends with a space, a reply-ack on a bulletin, a signpost that is not printable ASCII, a base-91 binary value over 255, and a third-party packet whose inner header has a tolerated defect.
Tests and tools:
- The neutral form reads and writes
commentonnmea. - The JSON writer shared by the tests and
pdn_aprs_diffdumpalways writes valid JSON: a non-finite number becomesnull(the dump wrote-nanfor anEQNS.coefficient), a locale's decimal comma never appears, and bytes that are not UTF-8 are written as their Latin-1 code points. A new test,json, checks this.
pdn_aprs 0.1.1
- Mic-E Rev 0 binary telemetry now gets an
obsolete-formatinfo, as the other obsolete formats do, and a value of 255 is refused on encoding, since it would be removed as 0xFF padding on the way back in. The ruling is shared by all five implementations (packet-net/aprs-vectors, "Mic-E Rev 0 binary telemetry").
pdn_aprs 0.1.0
The first release.
- Decodes TNC2 / APRS-IS lines and AX.25 UI frames: header, information field, data and diagnostics, for every APRS data type the conformance vectors cover.
- Lenient decoding by default, strict decoding, and each tolerable defect switchable on its own.
- Encodes every type, refusing what the spec does not allow, and checks that what it writes reads back the same. Mic-E encoding computes the destination address.
- Builder calls for the packets a station sends: position, object, item, Mic-E, weather, message, ack and reject, bulletin, status, telemetry and its PARM, UNIT, EQNS and BITS metadata, as TNC2 lines or AX.25 frames.
- Named symbols, with an overlay helper and descriptions.
- Device identification from the aprs-deviceid database, in an optional file (
pdn_aprs_deviceid.c, CC BY-SA 2.0 data). - Shipped as
pdn_aprs.handpdn_aprs.c, C99, no allocation, no global state, nothing locale-dependent. - Passes all 5,524 checks of packet-net/aprs-vectors at 16f12df, and agrees with the Rust implementation on every packet of a 6,879,893-packet APRS-IS capture.