River City ARCS weekly SSTV Net 8/30/17 9pm

This was my second time joining the weekly River City ARCS SSTV net and it had been a few weeks since the first time, so I had to go back and work out how I setup MMSSTV again. Since MMSSTV is a Windows app, on my Mac I booted WIndows 10 in Parallels and ran it from there. Unfortunately the combination of MMSSTV’s flaky soundcard settings and sharing a USB soundcard device with Windows in a Virtual Machine is probably not the best combination, but for future reference here’s the steps I used that finally worked:

  • Plug in the USB Rigblaster before starting Windows 10
  • From Mac System Preferences, Sound, set in and out to default mic and speakers, make sure Rigblaster not selected for either
  • Boot Windows 10 then from Parallels menu, Devices, Sound, select Rigblaster for both input and output
  • Start MMSSTV, go to Options, Setup MMSSTV, Misc tab, select Default for In and Out – this is shown in screenshot below:

The above steps seemed to work fine. Plugging in the Rigblaster after booting Windows 10 and then assigning it to the Windows 10 running VM when prompted by Parallels didn’t seem to work for me (although probably makes more sense), despite seeing the Rigblaster showup in the MMSSTV soundcard settings. No idea why this didn’t work.

Here’s a selection of the pictures received on the net this week:

Between each of these I was trying different settings and trying to get the Rigblaster selected in MMSSTV. By the time of the last couple of pictures sent on the net, I’d got the setting setup just right.

The River City Amateur Radio SSTV net is held weekly at 9pm, on the clubs 440Mhz repeater (which has coverage of Sacramento, CA and surrounding area). Details are on the club website here.

 

River City Amateur Radio Communications Society weekly SSTV net (06/21/17)

The River City Amateur Radio Communications Society in Sacramento runs a weekly SSTV net Wednesdays at 9pm local time (following the 2m net on the 2m N6NA repeater, and the 10m net) – I’ve tried to receive the pictures before but on 2m simplex between most of the stations in Sacramento area and out to my QTH in Davis, it’s a bit far to get a good copy, and some of the stations I can’t copy at all.

This week we tried something different and ran the net on the club’s 440MHz repeater. This worked great for me as we’ve got great coverage from this repeater over Sacramento area and surrounding area.

This was my first time to actively check-in on the net so I had a few things to learn on the fly! First, Multiscan 3B, what seems like one of the most common SSTV apps for the Mac, doesn’t seem to run reliably on current OS X 10.12.x versions. Last time I tried to use it I didn’t have any issue, but with the most recent MacOS version it would only start up the first time after it was installed, and every other time it crashed.

 

The first couple of pictures I received I realized I was receiving through the built in mic, and wasn’t even receiving via my Rigblaster interface. Understandably these first few pics were pretty terrible:

Part way through the net I switched to installing MMSSTV on Windows 10 running under Parallels on my Mac. My connection to my radio is through a Rigblaster, so I had to attach the Rigblaster input and output USB device to my Windows 10 guest. Once I configured it to receive and send through my Rigblaster interface, now I was receiving great images from the other ops on the net, and managed to send and get good reports on a couple of pictures myself:

 

 

 

 

 

 

 

Now I’ve got my config setup, I’m looking forward to our next SSTV net!

Building and running a Packet Radio Winlink solution in a Docker container, on a Raspberry Pi

Running Packet Radio apps in a Docker container, on a Raspberry Pi? Are you mad I hear you ask?Isn’t it hard enough to get ax25 and Packet Radio up and running on the Pi anyway? Having done this a few times already, this was my thinking, and I had the crazy idea that encapsulating most of the config and setup in Dockerfiles to build preconfigured containers might be an idea worth exploring.

Installing and configuring ax25 for the Raspberry Pi and Winlink clients that use ax25 like paclink-unix or PAT can be done and work well, but the steps, as for example documented in this comprehensive guide for building and installing paclink-unix which span several pages of instructions – this can be daunting even for those more familiar with building and installing apps from source on Linux.

Since the steps are well documented, I wondered if they could be captured in a Dockerfile to automate building a self-contained and ready to run Docker container.

tldr; The short story

I did eventually did get this working building ax25 from source and using Pat, but it took me down a rabbit hole for several hours. Skip to the end if you just want to find out how to build and run the completed Docker containers.

The Longer Explanation

I could not get ax25 to work self-contained in it’s own Docker container, as I ran into issues either accessing my serial device connected to my TNC Pi from inside the Container, and/or creating an ax0 interface when running kissattach.

If you expose the serial port on the Raspberry Pi to the Container running paclink-unix:

docker run -it --device=/dev/ttyAMA0 rpi-paclink

… When trying kissattach in the container it gives:

kissattach: Error setting line discipline: TIOCSETD: Operation not permitted

Are you sure you have enabled MKISS support in the kernel

or, if you made it a module, that the module is loaded?

Alternatively, starting with –privileged:

docker run -it --privileged  -v /dev/ttyAMA0:/dev/ttyAMA0  rpi-paclink

gives:

sudo kissattach /dev/ttyAMA0 1

kissattach: SIOCSIFMTU: No such device

I was initially trying to get this working because I wanted to run paclink-unix for Winlink email. Part of this app when you run the make script it will create wl2kserial and wl2ktelnet, but not wl2kax25. I had already run into this before, as it seems it doesn’t compile unless it has a later version of the ax25 stack compiled from source.

I changed gears and looked for how you could share an up and running ax25 stack from the Docker host, and it turns out this is easy to do, you just pass the –network=host param, and then ax0 appears in your network interfaces in your container.

The next issue I ran into is that configuring postfix as your email transport take some effort. bazaudi.com have a very detailed set of instructions, but I couldn’t get it working for outgoing email. It was working for incoming via wl2ktelnet and wl2kax25, but only for receiving emails and not sending. Time to try something else.

Installing and configuring Pat in a Container

I tried to get Pat working once before – I think I had it working on either a Debian or Ubuntu box, but couldn’t get it working on Raspbian on a Pi. I decided to try it again in this setup, and reusing the base image with ax25 already compiled from source, it was actually very easy to get Pat up and running.

This is dependent on having ax25 installed and configure on the host Pi OS, and the shared to the container with –network=host. I know, this seems redundant, but this is the only way I managed to get this working.

My base image for Raspbian including ax25 built form source is here: https://github.com/kevinhooke/DockerRPiAX25FromSource 

To build the image passing in the parameterized value for your callsign (passing your callsign in place of ‘yourcall’):

docker build --build-param MYCALL=yourcall -t rpi-ax25 .

Next build an image containing Pat, based on the image we just built – the source for this Dockerfile is here: https://github.com/kevinhooke/DockerRPiPATWinlink.git

Build this image with:

docker build --build-arg MYCALL=yourcall --build-arg MYCALLSSID=yourcall-1 
    --build-arg MYLOC=AA11aa --build-arg WINLINKPASS=yourwlpass 
    -t rpi-wl-pat .

Now to start it up remember we’re relying on an ax25 connection from the host, and we’re going to share it with the guest container. My TNC-Pi board connected to my Raspberry Pi is available on serial device as /dev/ttyAMA0, so I start up my ax0 port like:

sudo kissattach /dev/ttyAMA0 1 10.1.1.1

Next, run the container as a daemon, share the host networking, and expose port 8080 so we can access the Pat webapp:

docker run -d –network=host -p 8080:8080 rpi-wl-pat

Now let’s fire up the webapp:

Looks good, this is the Pat inbox. Let’s send a test email to myself – this is going to be sent using Packet over 2m VHF via my local Winlink gateway, AG6QO-10. I have this preconfigured in my Pat config file. You can configure this yourself before creating the rpi-wl-pat image:

Remember the Pat webapp that we’re interacting with here is running in a Docker container, on a Raspberry Pi. I just happen to be accessing it remotely from my Mac. For mobile operation or out in the field, you could attach a touchscreen to the Pi and connect a keyboard and mouse too.

To send my email over RF to the Winlink gateway, click Action, then Connect:

In the Pat status window we now see a log of the Packet exchange between my station and AG6QO-10 via BERR37:

A few seconds later the email arrives in my gmail inbox:

If I reply to the email in gmail, it will go back over the Winlink network, and be waiting for me when I connect to the Winlink gateway again over RF. Let’s give that a go in Pat – select Action and Connect, we connect to AG6QO0-10 again over 2m VHF, and now the reply is in my inbox in Pat:

Success!

Building an Amateur Radio Packet to Twitter bridge: Part 3 – preventing duplicate tweets

In my last post (part 2, also see part 1 here), I talked about Twitter’s API rate limits. Since many Packet Radio transmissions are duplicates by their nature, for example, beacon packets and ID packets, it’s important to have some kind of mechanism to prevent sending these through to Twitter.

The approach I used was to insert each received packet into a MongoDB database, storing the received packet data, who the packet was from and who to, and additional metadata about the packet, for example, when last sent, and when it was last received.

Here’s an example of what each document stored looks like:

{
 "_id" : ObjectId("5909828e5a2f130fc8039882"),
 "firstHeard" : ISODate("2017-05-03T07:11:10.051Z"),
 "from" : "AE6OR ",
 "heardTimes" : 1,
 "infoString" : "Š¤¤@à–¤ˆŽ@@à–„Š¤¤@ஞžˆ²@`–„Š¨@`¨‚žŠ@a\u0003ðHello from 5W Garage packet node AUBURN 73's Steli !\r",
 "lastHeard" : ISODate("2017-05-03T07:11:10.051Z"),
 "lastTweet" : ISODate("2017-05-03T07:11:10.051Z"),
 "to" : "BEACON",
 "tweet" : "Received station: AE6OR  sending to: BEACON : Š¤¤@à–¤ˆŽ@@à–„Š¤¤@ஞžˆ²@`–„Š¨@`¨‚žŠ@a\u0003ðHello from 5W Garage packet node AUBURN 73's Steli !\r"
}

My current logic to check for duplicates and record when a tweet is last sent is:

    1. search for a matching document (tweet details) with a $lt condition that lastTweet is before ‘now – 12 hours’:
      document.lastTweet = {'$lt' : moment().utc().subtract(12, 'hours').toDate() };
    2. This is executed as a findOne() query:
      db.collection('tweets').findOne(document).then(function (olderResult) {
         ...
      }
    3. If an existing document older than 12 hours is found, then update properties to record that this same packet was seen now, and the time is was resent was also now (we’ll resend it to the Twitter api after the db updates):
      if (olderResult != null) {
          sendTweet = true;
          updateProperties = {
              lastHeard: now,
              lastTweet: now
          };
      }
      else {
          updateProperties = {
              lastHeard: now
          };
      }

      If not older than 12 hours, set properties to be updated to indicate just the lastHeard property

    4. To either update the existing document or insert a new document if this was the first time this packet was heard, do an ‘upsert’:
      db.collection('tweets').update(
          document,
          {
              $set: updateProperties,
              $inc: {heardTimes: 1},
              $setOnInsert: {
                  firstHeard: now,
                  lastTweet: now
              }
          },
          {upsert: true}
      ).then(function (response) {
      ...
      }
    5. Depending on the result indicating if we inserted or updated, set a property so on return we know whether to send a new Tweet or not:
      if(response.upserted != null || sendTweet) {
          response.sendTweet = true;
      }
      else{
          response.sendTweet = false;
      }

The approach is not yet completely foolproof, but it is stopping the majority of duplicate Tweets sent to Twitter so far.

For the full source check the project on github here: https://github.com/kevinhooke/PacketRadioToTwitter .