npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

th-d72-ax25

v1.3.13

Published

A AX25 TNC Package for the Kenwood TH-D72A Packet Radio

Downloads

35

Readme

th-d72-ax25

A KISS & AX.25 packet radio stack for node.js. Curated for the Kenwood TH-D72A HT. The TH-D72A is one of the few hand-held transceivers that has a built in TNC and adheres to AX.25 protocol.

This is a merge of code from @echicken (ax25) and work from another one of it's branches

This is a work in progress. Thanks alot to @echicken for guidance on the code!


Installation

npm i th-d72-ax25

Dependencies

node-serialport

Compatibility

ax25.kissTNC should work with any KISS TNC, and has been tested with several different models.

Yes, ax25.kissTNC has been tested and found to work with soundmodem, which emulates a KISS TNC.


The Stack


  • Notes
    • Notes on using ax25.Packet and ax25.Session without ax25.kissTNC

ax25.kissTNC

var tnc = new ax25.kissTNC(
	{	serialPort : "COM3",
		baudRate : 9600,
		txDelay : 500,
		persistence : .25,
		slotTime : 500,
		txTail : 100,
		fullDuplex : false
	}
);

The serialPort and baudRate argument properties are required. The rest are optional, and can be set after the fact.

var ax25 = require("ax25");

var tnc = new ax25.kissTNC(
	{	serialPort : "COM3",	// Serial device, eg. "COM3" or "/dev/ttyUSB0"
		baudRate : 9600			// Serial comms rate between computer and TNC
	}
);

tnc.on(
	"frame",
	function(frame) {
		console.log("Here's an array of bytes representing an AX.25 frame: " + frame);
	}
);

tnc.on(
	"error",
	function(err) {
		console.log("HURRRRR! I DONE BORKED!" + err);
	}
);

Events:
  • opened
    • The connection to the TNC has been opened successfully.
  • closed
    • The connection to the TNC has been closed.
  • error
    • An error has occurred (error details will be supplied as an argument to your callback function.)
  • frame
    • A KISS frame has been received from the TNC (the enclosed AX.25 frame, less start/stop flags and FCS, will be supplied as an argument to your callback function.)
  • sent
    • A KISS frame was sent to the TNC (the number of bytes sent to the TNC will be supplied as an argument to your callback function. Not very useful.)

Properties:
  • serialPort
    • eg. "COM1", or "/dev/ttyUSB0". (String)
  • baudRate
    • eg. 1200, 9600, 115200. (Number)
  • txDelay
    • Transmitter keyup delay, in milliseconds. Default: 500. (Number)
  • persistence
    • Persistence, float between 0 and 1. Default: 0.25. (Number)
  • slotTime
    • Slot interval, in milliseconds. Default : 100. (Number)
  • txTail
    • Time to keep transmitting after packet is sent, in milliseconds (deprecated.) (Number)
  • fullDuplex
    • Boolean, default: false. (Boolean)

Methods:
  • send(frame)
    • Sends an AX.25 frame to the TNC to be sent out over the air. (frame must be an array of unsigned 8-bit integers, representing an AX.25 frame less the flags and FCS, eg. the return value of ax25.Packet.assemble().)
  • setHardware(value)
    • Most people won't need to use this ... consult your TNC's documentation.
  • close()
    • Close the connection to the TNC.
  • exitKISS()
    • Bring the TNC out of KISS mode (if your TNC has a terminal mode.)

ax25.Packet

var packet = new ax25.Packet({ 'frame' : frame });

or

var packet = new ax25.Packet(
	{	'destinationCallsign'	: "VE3XEC",
		'destinationSSID'		: 1,
		'sourceCallsign'		: "KB1YFO",
		'sourceSSID'			: 0,
		'repeaterPath'			: [
			{ 'callsign' : "VE7RRX", 'ssid' : 7 },
			{ 'callsign' : "K6BSD", 'ssid' : 2 },
			{ 'callsign' : "WX6YYZ", 'ssid' : 8 },
			{ 'callsign' : "KF5PFU", 'ssid' : 3 },
			{ 'callsign' : "KB1YFO", 'ssid' : 10 }
		],
		'pollFinal'				: true,
		'command'				: true,
		'type'					: ax25.Defs.I_FRAME,
		'nr'					: 1,
		'ns'					: 3,
		'pid'					: ax25.Defs.PID_NONE,
		'infoString'			: "Your mother."
	}
);

In the first example, the argument's frame property would be an array of unsigned 8-bit ints, such as provided by the ax25.kissTNC's frame event.

The second example shows how you can assign values to all of the ax25.Packet object's properties upon instantiation. If no argument is provided, these properties will be set to their default values, and you should set them as needed before calling ax25.Packet.assemble().

var util = require("util");
var ax25 = require("ax25");

var tnc = new ax25.kissTNC(
	{	'serialPort' : "/dev/ttyUSB0",
		'baudRate' : 9600
	}
);

var beacon = function() {
	var packet = new ax25.Packet(
		{	sourceCallsign : "MYCALL",
			destinationCallsign : "BEACON",
			type : ax25.U_FRAME_UI,
			infoString : "Hello world!"
		}
	);
	var frame = packet.assemble();
	tnc.send(frame);
	console.log("Beacon sent.");
}

tnc.on(
	"error",
	function(err) {
		console.log(err);
	}
);

tnc.on(
	"opened",
	function() {
		console.log("TNC opened on " + tnc.serialPort + " at " + tnc.baudRate);
		setInterval(beacon, 30000); // Beacon every 30 seconds - excessive!
	}
);

tnc.on(
	"frame",
	function(frame) {
		var packet = new ax25.Packet({ 'frame' : frame });
		console.log(
			util.format(
				"Packet seen from %s-%s to %s-%s.",
				packet.sourceCallsign,
				packet.sourceSSID,
				packet.destinationCallsign,
				packet.destinationSSID
			)
		);
		if(packet.infoString != "")
			console.log(packet.infoString);
	}
);

Properties
  • destinationCallsign
    • The destination callsign, up to six alphanumerics. (String)
  • destinationSSID
    • The destination SSID, one number, 0 - 15. (Number)
  • sourceCallsign
    • The source callsign, up to six alphanumerics. (String)
  • sourceSSID
    • The source SSID, one number. (Number)
  • repeaterPath
    • An array of { callsign : , ssid : } objects. (Array of objects)
  • pollFinal
    • True if this is a poll/final packet, false otherwise. (Boolean)
  • command
    • True if this is a command packet, false otherwise. Inverse of response. (Boolean)
  • response
    • True if this is a response packet, false otherwise. Inverse of command. (Boolean)
  • type
    • Bitfield for comparison against packet types as defined in Defs.js (eg. U_FRAME, I_FRAME, S_FRAME.) (Number)
  • nr
    • Sender's receive-sequence number (N(R) in the AX.25 2.2 spec.) (Number)
  • ns
    • Sender's send-sequence number (N(S) in the AX.25 2.2 spec.) (Number)
  • pid
    • Protocol ID field, for comparison against PIDs defined in Defs.js. (Number)
  • info
    • The information field of an I or UI frame. (Array)
  • infoString
    • The information field of an I or UI frame, as a string. (String)

Methods
  • disassemble(frame)
    • Where frame is an array of unsigned 8-bit integers representing an AX.25 frame (eg. the value provided by the ax25.kissTNC frame event,) disassemble frame and populate the above properties with the values found therein. (Note: if ax25.Packet is instantiated with a frame argument, this will happen automatically.) (Void)
  • assemble()
    • When creating an outgoing frame, make a new ax25.Packet object, populate its properties as desired, then call ax25.Packet.assemble(), which will return an array of numbers representing an AX.25 frame (which can be supplied to ax25.kissTNC.send(frame).) (Array)
  • log()
    • Returns a line of text describing some of the packet's properties, suitable for logging purposes. (String)

ax25.Session

Events
  • packet
    • An outgoing packet is ready for transmission. Your callback will be provided with an ax25.Packet object which can be sent with ax25.kissTNC.send(packet.assemble());
  • data
    • Data (I or UI frame payload) has been received from the remote station. Your callback will be provided with an array of uint 8 bytes. (ax25.Utils.byteArrayToString(arr) can turn this into a string for your convenience.)
  • connection
    • The connection state has changed. Your callback will be provided with a boolean value. True means that a connection has been established. False means that the connection has been closed. (Note that the connection may occasionally be re-established without a disconnection happening as part of a reset procedure.)
  • error
    • Something done borked. Your callback will be provided with a helpful textual error message.

Properties
  • connected
    • Whether or not a connection has been established with the remote station. (Boolean)
  • connection
    • More fine-grained connection-state info than connected. (Number)
      • 1 = Disconnected. (Not connected, not trying to connect or disconnect.)
      • 2 = Connected. (Connected and ready.)
      • 3 = Connecting. (Attempting to connect, waiting for acknowledgement or timeout.)
      • 4 = Disconnecting. (Requested disconnect, waiting for acknowledgement or timeout.)
  • remoteCallsign
    • The remote station's callsign, up to six alphanumerics. (String)
  • remoteSSID
    • The remote station's SSID, one number, 0 - 15. (Number)
  • sourceCallsign
    • The local station's (your) callsign, up to six alphanumerics. (String)
  • sourceSSID
    • The local station's SSID, one number, 0 - 15. (Number)
  • repeaterPath
    • An array of { callsign : , ssid : } objects. (Array of objects)
  • maxFrames
    • Maximum number of unacknowledged I frames out at any given time. Default: 4. (Number)
    • If in modulo 128 mode, the maximum value is 127; otherwise the maximum is 7.
  • packetLength
    • Maximum packet payload size, in bytes, minimum of 1. Default: 256. Smaller values such as 64 are best for crappy links. The spec says 256 is the maximum, so don't expect most TNCs to support larger values. (Number)
  • retries
    • How many times to poll the other station for a response before giving up. Default: 5. You may wish to raise this value if using a very busy frequency, etc. (Number)
  • hBaud
    • The baud rate of over-the-air communications. Default: 1200. It's recommended that you set this if your value differs from the default, as polling intervals and other timeouts are calculated based on this figure, among others. (Number)
  • modulo128
    • (Boolean)
    • If set prior to a connect() attempt, will attempt to send a SABME command to initiate a connection with modulo 128 sequence numbers.
    • If read during the connected state, indicates whether the sequence numbers for this session are modulo 128. (Could be useful for tweaking the maxFrames value.)
    • Modulo 128 implementation is in progress. Setting this value will have undesirable results for the time being.

Methods
  • connect()
    • Opens a connection to another station. Remote and Local callsign and SSID properties must be set first. (Void)
  • disconnect()
    • Disconnect from the remote station. (Void)
  • send(info)
    • Send array of bytes (uint 8) 'info' to the remote station. (Void) (Note: 'info' is just a plain old Array(). We may switch to Uint8Array or Buffer at some point.)
  • sendString(str)
    • Send string 'str' to the remote station. (Void)
  • receive(packet)
    • Process and respond to the received (and disassembled) packet 'packet'. (Void)

Notes on using ax25.Packet and ax25.Session without ax25.kissTNC

If you're receiving AX.25 frames from something other than a KISS interface attached to a serial port, you can still use ax25.Packet for packet assembly/disassembly, and you can also use ax25.Session to maintain stateful connections.

ax25.Packet.disassemble(frame) expects frame to be a plain array of unsigned 8-bit integers representing an AX.25 frame less the start/stop flags and Frame-Check Sequence (FCS.) Either your interface or some middleware must remove the flags and verify the FCS, then format the data as described before passing it off to this method.

ax25.Packet.assemble() returns an array of unsigned 8-bit integers representing an AX.25 frame without start/stop flags or an FCS. Either your interface or some middleware must calculate and append the FCS to the frame, then prepend and append flags and convert the array as needed.

I know that these arrays of uint8s are kinda dumb. I didn't really know about Buffer or Uint8Array when I started this thing, and I'm too lazy to make those changes right now.


To Do:

  • ax25.Session
    • if attempted SABME but failed and reconnecting with SABM, reset maxFrames to sane value
    • Implement oustanding portions of AX.25 2.2 spec sections 4.3.2.4, 4.4.4, 6.4.4.2, 6.4.4.3, 6.4.8 re: sending and receiving SREJ
      • add SREJ case to Session.receive()
        • retransmit the requested frame, with a retry event scheduled
        • track retransmitted frames and their timed events
        • clear timed event when remote N(R) > the retransmitted frame
      • send SREJ if:
        • settings.modulo128 is true
        • state.sentREJ is false
        • a properly-sequenced packet has been received this session
        • any out-of-sequence packet is received
      • set state.sentSREJ an SREJ is sent (code is in place to not send REJ if this is true)
      • if settings.modulo128 is true, push received but out-of-sequence I frames into state.receiveBuffer to be handled when previously missed frames have been received
      • push into an array the sequence numbers of frames that have been requested for retransmission via SREJ, splice them out once they have been received, clear state.sentSREJ condition once this array is zero-length
      • schedule timed event for retransmitting SREJ, do not push sequence number into SREJ-request tracking array additional times
      • implement various P/F C/R minutiae and any other remaining portions of the above-mentioned parts of the specification
      • use of SREJ probably doesn't need to hinge on modulo 128 operation; remote capability should be determined by XID parameter negotiation if/when that gets implemented.
      • until XID is implemented, possibly watch for returned SREJ frames in U_FRMR frames from remote, sending plain REJ instead (if that's the expected response)
  • Implement XID frame type in Packet.js and Session.js (placeholders currently exist)
    • In ax25.Session, various items such as maxFrames, modulo 128 operation, SREJ compatibility, etc. will need to be refactored, with decisions made based on a store of remote capabilities
  • YAPP file transfers in Session or additional submodule
    • Or some other file transfer mechanism if something better came along and has seen some uptake