Wiring
How DirewolfMain (internal/direwolf/direwolf.go) builds the components of samoyed-direwolf
and connects them.
No component reaches another through a package-level variable:
each is handed what it uses when it is made,
as a constructor argument.
Each arrow points from a component to something it uses,
and is labelled with what it is handed:
a function, often a method value such as dataLinkQueue.RecFrame,
or a small interface declared beside the code that uses it.
Dashed arrows are connections made after construction rather than at it.
The diagram leaves out what is plain configuration
(the parsed configuration file and the debug options,
and the digipeater.Port for each channel that RadioConfig.digipeaterPorts takes from it for the digipeaters)
and the services with only one user,
which DirewolfMain hands over in the same way:
the heard list (mheard) to the packet filter, the IGate, the beacon service and the received packet handler;
the packet filter to the IGate and the digipeaters;
the APRS log to the beacon service and the received packet handler;
the GPS to the beacon service and the waypoint sender;
and the waypoint sender and the APRS decoder to the received packet handler.
How a received frame gets from a sample to recv_process,
and what happens to it there,
is on the Receive path page.
Construction order
DirewolfMain makes each component once what it is handed exists,
so the order of construction follows the arrows:
The audio devices, then the
DataLinkQueue, which almost everything feeds.The demodulators (
multi_modem_init), handed aradioSinkthat passes their frames to theDataLinkQueue.The network TNCs, the serial TNCs and the AXUDP channels. Their listeners start straight away and can hand the queue frames at once.
The
TransmitQueue, which sends a packet for a network TNC, serial TNC or AXUDP channel straight there rather than queueing it.The tone generators and the PTT.
The web interface and the AGW server. The web interface binds its port as it is made; with its port disabled, or taken, there is no hub, and the frames that would have gone to it go nowhere. The AGW server is made here, so that the transmit service can show its clients each frame sent, but only started once the transmit threads are running. It binds its port when it is made, so a port already in use is reported before the server starts, rather than once it is running. With the port disabled or taken, or when
-xonly sends calibration tones, there is no AGW server: what it would have been handed takes a nil one as having no clients.The
XmitService, which starts a transmit thread per radio channel.The IGate and the two digipeaters, then the
AX25Link, the KISS handler and its transports, the APRStt gateway and the beacon service.The audio device goroutines (
recv_init), and finallyrecv_process, which takes from theDataLinkQueueand hands each item to theAX25Linkor therecPacketHandler.
Nothing is sent or received until the goroutines that do so are started, so a component made later in this list can still be handed to one made earlier, by a connection made after construction, before anything starts. The next section lists those connections.
Connections made after construction
The PTT and the demodulators.
The PTT mutes a channel’s receiver while it transmits (Layer2Receiver.MuteInput),
so it is made after the demodulators.
The demodulators in turn set the PTT’s DCD output (radioSink.setDCD)
and read its transmit inhibit input (Layer2Receiver.txInhibited),
so those two are set once the PTT exists.
The transmit queue and the IGate.
The IGate queues packets from APRS-IS for transmission,
and the transmit queue hands the IGate whatever is queued for the IGate’s own channel.
The queue is made first,
and handed the IGate’s sendRecPacket with TransmitQueue.SetIGate once the IGate exists.
The KISS handler and the KISS TCP service.
The handler copies a client’s data frames to the TCP clients with KISSCOPY,
so it is made after the KissNetService and handed it.
The service is handed the handler when it is started (KissNetService.Start),
rather than when it is made.
It binds its ports when it is made, though,
so a port already in use is reported before the service starts,
rather than once it is running.
The audio devices and the demodulators.
Each audio device’s statistics report its channels’ received audio levels,
which the demodulators keep,
so the devices are handed Layer2Receiver.AudioLevel with AudioDevices.setAudioLevel once the demodulators exist.
Unlike the others this is not a cycle:
the demodulators do not use the audio devices,
and are made after them only because DirewolfMain opens the devices first.
Conventions
The shapes in the diagram follow the dependency conventions in AGENTS.md:
A component that makes one call is handed a function, typed for what it does with it:
frameReceiver,dwcore.FrameSender,igateSender,seizeConfirmer.A component that makes several calls is handed a small interface, declared beside it and holding only those calls:
linkClients,linkTransmitter,clientTransmitQueue,xmitQueue,agwDataLink.*TransmitQueue,*DataLinkQueueand*AGWServersatisfy those interfaces without knowing about them.
References
internal/direwolf/direwolf.go:DirewolfMain, which makes and connects everything above, andrecPacketHandler.The constructors, and the functions and interfaces they take, in
internal/direwolf:multi_modem.go(multi_modem_init,ReceiveSink,radioSink),dlq.go(frameReceiver,seizeConfirmer),tq.go(NewTransmitQueue,clientTransmitQueue,SetIGate),xmit.go(NewXmitService,xmitQueue),ax25_link.go(NewAX25Link,linkClients,linkTransmitter),server.go(NewAGWServer,agwDataLink),igate.go(NewIGate,igateSender),kiss_frame.go(NewKissHandler),kissnet.go(NewKissNetService,KissNetService.Start),aprs_tt.go(NewTTGateway),beacon.go(NewBeaconService,beaconIGate),audio.go(AudioDevices.setAudioLevel),channel_config.go(RadioConfig.digipeaterPorts) andrecv.go(recv_init,recv_process).internal/direwolf/webui_source.go:webui_init, andinternal/webui/server.go:NewServer.internal/ptt/ptt.go:New, handed each channel’s configuration byRadioConfig.pttChannels(internal/direwolf/channel_config.go).The digipeaters’ constructors, and what they take, in
internal/digipeater:digipeater.go(New,Port) andcdigipeater.go(NewConnected).internal/dwcore/dwcore.go:FrameSender, shared by everything that hands the transmit queue a frame.AGENTS.md, the “Dependencies” section.