2024-08-11

PIO and Interrupts

Introduction

I want to be able to raise system level interrupts from PIO programs and there aren’t too many examples of this, so here is my simple contribution.

PIO program

First, the PIO program, which simply waits for a pin to change and then immediately raises PIO IRQ 0.

.program irq_example
.wrap_target

wait 0 pin 0	; wait for a 0 on IN pin 0
irq nowait 0	; raise PIO IRQ 0

.wrap

Compile this with pioasm then #include the .h in your C program.

C Program

Interrupt Setup

There are four system level interrupts related to PIO: PIO0_IRQ_0, PIO0_IRQ_1, PIO1_IRQ_0, PIO1_IRQ_1. We shall use PIO0_IRQ_0.

First we attach an interrupt handler to PIO0_IRQ_0:

void isr(void) {
	// clear PIO IRQ 0 that was raised by irq nowait 0
	pio_interrupt_clear(pio0, 0);
}

irq_add_shared_handler(PIO0_IRQ_0,
                       isr,
                       PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);

It is important that pio_interrupt_clear() is called otherwise the CPU will lock up constantly executing the interrupt handler (aka ISR - interrupt service routine)

We then enable the interrupt:

irq_set_enabled(PIO0_IRQ_0, true);

Finally we configure PIO0_IRQ_0 to be triggered when PIO IRQ 0 is raised:

pio_set_irq0_source_enabled(pio0, pis_interrupt0, true);

The functions used above are described in the Pico C SDK documentation.

PIO Setup

The PIO program can now by installed the usual way by first setting up the state machine:

uint sm = pio_claim_unused_sm(pio0, true);
uint offset = pio_add_program(pio0, &irq_example_program);
pio_sm_config cfg = irq_example_program_get_default_config(offset);

Configure and connect GPIO to the state machine:

// connect GP 2 to IN of state machine
sm_config_set_in_pins(&cfg, 2);

// it is an input pin
pio_sm_set_consecutive_pindirs(pio0, sm, 2, 1, false);

// we want pull up on the pin
gpio_pull_up(2);

Now we are ready to start the state machine:

pio_sm_init(pio0, sm, offset, &cfg);
pio_sm_set_enabled(pio0, sm, true);

PIO ASM and PlatformIO

Introduction

PIO

The RP2040 has two programmable IO (PIO) blocks, each capable of executing 4 programs in parallel. This allows the RP2040 great flexibility in mix and matching different protocols. Want 3x CANBUS and 1xSPI? RP2040 got you. Want 5xI2C instead? That’s cool too. It is a very flexible approach vs having hardwired peripherals and since PIO execute independently of the CPU, nicer and faster than using the CPU to do bitbang.

PIOs are essentially hyperspecialised CPUs and they have their own assembly language which is parsed by a program called pioasm, part of the pico-sdk. pioasm converts PIO programs in assembly into C-headers which can then be #included and used. Typically you have your PIO programs in a .pioasm file which is then transpiled into a .h file by pioasm.

PlatformIO

PlatformIO is a very nice platform for doing embedded development. It nicely packages up all the toolchains and dependencies for you and sets everything up all neat and tidy.

PlatformIO and pioasm

While PlatformIO knows how to compile C code, it doesn’t natively support pioasm, which means you need to remember to manually invoke pioasm before building with PlatformIO, or configure PlatformIO to run a pre-build step that runs pioasm against all .pioasm.

Alternatively, one can use make which I have opted to do since it feels natural - after all make is designed for this kind of work.

With the following Makefile, all .pioasm files are compiled into their .h counter-part prior to building the program. The Makefile wraps common PlatformIO tasks to provide additional convenience.

PIOASM=/path/to/pioasm

%.h: src/%.pioasm
        $(PIOASM) $< src/$@

build: pio_trigger.h
        pio run

upload: build
        pio run -t upload

This script assumes your source code is in /src and you have a copy of pioasm.

Building pioasm

Because I use the community developed arduino-pico core, a copy of pico-sdk was already available in $HOME/.platformio/packages/framework-arduinopico/pico-sdk/, including the pioasm source code. pioasm can then be built as follows (assuming you are on a unix-like):

cd $HOME/.platformio/packages/framework-arduinopico/pico-sdk/tools/pioasm/
mkdir build
cd build
cmake ..
make

This will produce the pioasm binary in the build/ directory. You can leave it there or move it.

2024-01-23

Estimating a Supernova's Brightness as an Amateur

Introduction

2023rve is the designation given to a Type II supernova discovered on 2023-09-08 by Mohammad Odeh from UAE. It appears to be part of NGC 1097, a galaxy 45 million light years away. Due to it’s brightness, 2023rve was visible to budget amateur astronomers/astrophotographers like myself.

Below is my first image of 2023rve.

2023rve

This is a stacked image consisting of 64x60s exposures captured using an unmodified Canon 800D attached to an Evostar 72ED. I was and continue to be very excited about this image because supernovas occur but rarely and are generally out of reach for budget setups like mine. Catching glimpse of one is not unlike seeing an unicorn.

Light Curves

2023rve is a type II supernova, and its light curve will look like one of the following depending on its subclassification (II-P, II-L, IIn or IIb).

Type II-P and type II-L supernova lightcurves

It occurred to me then that with sufficient images I can have a go at generating such a light curve and figure out what kind of supernova 2023rve is! This is a particular exciting idea to me because it takes supernovas from the realm of theory (knowing about them) into reality (studying them).

In the time since then, I managed 4 captures in total, including the one above, and this post documents my efforts in estimating the magnitude of 2023rve from my data and using that to construct a light curve.

General Approach

The idea is to calibrate my data, which are 60 second exposures of 2023rve, against stars of known visual magnitude. By comparing the pixel values that make up 2023rve vs these calibration stars, I can estimate its apparent visual magnitude. Then, making the assumption 2023rve is part of NGC1097, we can use the distance to NGC1097 to calculate the absolute magnitude of 2023rve and generate a light curve.

Identifying Stars

The first step is to find stars and their known magnitudes. While bright stars such as Rigel or Sirius have well known magnitudes, the stars in the vicinity of NGC1097 are less well known and unnamed. In order to look up stars in astronomical databases, we need their right-ascension (RA) and declination (DEC) coordinates. These coordinates serve the same purpose as GPS coordinates of latitude and longitude: they unique identify a position in the sky.

To locate our stars, I loaded my images into Siril and carried out platesolving, a method of locating images within the sky by looking at the pattern of the stars within the image.

Screenshot 2024-01-23 at 12.11.39 pm

Once platesolving is completed, I saved the file as a FITS file which embeds the coordinate information into the file metadata. I then open the FITS file in AstroImageJ and by manually adjusting the upper and lower limits on the histogram in the image viewer and zooming in, I am able to get a good view of NGC1097.

Screenshot 2024-01-23 at 12.50.16 pm

By right clicking on the image, a link to SIMBAD will open which allows all known stellar objects in the vicinity to be queried. Using this, three objects with known visual magnitudes were identified and annotated.

Screenshot 2024-01-23 at 12.49.26 pm

These are our reference stars and their visual magnitude is tabulated below.

Object Vmag
CD-30 1031 10.3
TYC 7011-484-1 11.39
TYC 7011-1028-1 12.15

Measuring Brightness

In order to relate the brightness of these stars to the measure pixel values, I used AstroImageJ’s single-aperture photometry tool, which samples a central circular region and an annulus around it. The central circular region should just fit the star while the annulus should contain only background. The background pixel value is estimated from the pixels within the annulus and subtracted from the stellar pixel values. This value is reported as Int Cnts. This measurement was done on each of the reference stars and 2023rve, e.g.

Screenshot 2024-01-22 at 12.32.31 pm

Deriving Visual Magnitude

Tabulating the Int Cnts obtained across the 4 observations produces the following:

CD-30 1031 TYC 7011-484-1 TYC 7011-1028-1 2023rve
2023-09-18 1.09E+06 4.54E+05 2.43E+05 2.77E+04
2023-09-24 1.09E+06 4.67E+05 2.60E+05 2.49E+04
2023-11-18 9.65E+05 4.32E+05 2.64E+05 1.20E+04
2024-01-22 1.14E+06 5.47E+05 2.73E+05 1.06E+03

It is quite pleasing that the Int Cnts values for the three reference stars are relatively stable, particularly as I am not using an uncooled sensor nor did I control for atmospheric conditions. Of the three reference stars, TYC 7011-1028-1 had the fewest saturating pixels and was the most stable over time, and was chosen as the reference against which to measure 2023rve’s brightness:

TYC 7011-1028-1 2023rve (2023rve) / (TYC 7011-1028-1)
2023-09-18 2.43E+05 2.77E+04 0.114
2023-09-24 2.60E+05 2.49E+04 0.096
2023-11-18 2.64E+05 1.20E+04 0.045
2024-01-22 2.73E+05 1.06E+03 0.004

By expressing 2023rve’s brightness in terms of TYC 7011-1028-1 I am implicit assuming the my sensor’s response is approximately linear for the brightness being measured.

Magnitudes are logarithms of ratios, so the brightness ratio in the table above turns into a difference in magnitude given by mag_diff=-2.5log10(x)) where x are the values in the final column. By adding this value from the visual magnitude of TYC 7011-1028-1 we arrive at an estimated visual magnitude for 2023rve:

(2023rve) / (TYC 7011-1028-1) mag_diff Vmag
2023-09-18 0.114 2.355 14.505
2023-09-24 0.096 2.545 14.695
2023-11-18 0.045 3.355 15.505
2024-01-22 0.004 6.030 18.180

Verification

The estimated visual magnitude of 2023rve looks quite reasonable, but is it correct? Thankfully professional astronomers have made their own measurements which I have collected below:

Date Vmag
2023-09-06 14.5
2023-09-11 13.9
2023-09-16 14.43
2023-09-24 14.390
2023-11-15 15.500
2024-01-06 16

Sources:

  1. https://www.wis-tns.org/astronotes/astronote/2023-247
  2. https://www.rochesterastronomy.org/supernova.html#2023rve
  3. https://www.flickr.com/photos/snimages/53209027493/in/photostream/
  4. http://gsaweb.ast.cam.ac.uk/alerts/alert/Gaia23cmp/

Compared against these, my visual magnitudes estimates compare quite favourably! I am pleasantly surprised how well things worked out. My last measurement, on 2024-01-22, stands out by how much it differs. It is also the measurement I have the least confidence in as by that point I could no longer reliably identify 2023rve in a single 60s exposure. I have some ideas here: take longer exposures or stack my exposures carefully (keeping values linear) and estimate from the stacked image.

Absolute Magnitude

The conversion to absolute magnitude is quite straight-forward as is the subsequent plotting.

Screenshot 2024-01-23 at 1.43.29 pm

Note that because I don’t know when peak brightness occurred, I am using days-since-discovery instead.

Type II Classification

I don’t think there is enough data yet to definitively say what subtype 2023rve is. More data points will decide the matter and I look forward to the next set of clear skies so I can continue this effort.

2023-12-23

On NS*DateFormatters

There now exists NSDateFormatter and NSISO8601DateFormatter and their behaviour when a timezone is not set is not the same: NSDateFormatter produces strings formatted for local time while NSISO8601DateFormatter produces string formatted for UTC.

As an example, given a date-time of 2000-12-31T23:00:00Z

NSDateFormatter with no timezone set and full styles:
Monday, January 1, 2001 at 10:00:00 AM Australian Eastern Daylight Time
NSISO8601DateFormatter with no timezone set:
2000-12-31T23:00:00Z

Note that NSDateFormatter "applies" my local timezone automatically whereas NSISO8601DateFormatter doesn't. Sidebar: it seems that while NSDate carries no timezone information, it is always interpreted as UTC time.

2023-10-16

DIY EQMod Direct USB Cable

I am in the process of resurrecting my Eq5 Pro so it is compatible with ASIAir Mini. The first step is to build an EQMod Direct USB cable. While it is possible to buy one pre-made, since I had all the parts I decided to build it myself.

Based on the reference document I need to make an single-ended RJ45 cable. I followed T-568A colour scheme:

Based on this colour scheme, the connection to a USB-UART adapter is as follows:

RJ45ColourUSB-UART
4BlueGround
5Blue-WhiteRXI
6OrangeTXO

Plug the RJ45 end of the cable into the hand-controller port on the control box and the other end into the ASIAir Mini. Then power up the ASIAir Mini as usual and use one of the 12V outputs to power the SynScan. Make sure to switch on the SynScan as it has its own power switch. After this select EQMod for the mount model and leave the baudrate at the default value of 9600. Tap the enable-toggle-switch and the ASIAir Mini will attempt to connect to the mount. After a successful connection you should be able to manually slew the mount in RA and DEC in Preview mode.

Update: I have to power up the SynScan after the ASIAir has fully booted (made it's beeping noises) in order for them to communicate successfully. If both are powered up at the same time, something goes awry and the ASIAir Mini will not be able to connect.

2023-09-18

Build Log: SCD41 CO2 Sensor

CO2 concentration is a proxy for how well a space, especially an enclosed one, is ventillated. To this end I build a quick-n-dirty CO2 sensor using a SCD41 breakout board from pimoroni and bits and pieces I had in the workshop.

Took the opportunity to practice making enclosures, especially for an irregularly shaped circuit. The enclosure was designed in FreeCAD by taking a picture of the circuit then importing it as a reference image. After correct for scale the designed enclosure was reasonable accurate, needing only some small tweaks to dimensions to correct forperspective distortion. The result was then 3D printed on my FF Creator Pro.

The enclosure provides openings for a button (currently unused), a "cage" for the CO2 sensor, display and micro-USB port.

In addition to practicing painting techniques, I also wanted to try new ways of "labelling" my projects. Water-slide decals are widely used in model kits, and it turns out you can buy blank sheets with which to make your own decals with! I picked a cheap'ish option off Amazon. Just search for "water slide decal clear laser". Once they arrived it was a simple matter of designing the decals in inkscape, printing it out and cutting them to size. To complete the build, a plastic primer was applied, followed by colour coats, then a clear coat was applied, followed by the decal, followed by 2 more layers of clear coat.

Overall quite happy with the outcome! Could have done more sanding before applying paint to hide the layer lines better and the a matte coat I think would have worked better. Nonetheless the decal idea worked out well and I think it will be my goto - a big improvement over permanent markers.

2023-03-29

Unexpected Latency in Tinyproxy

I love tinyproxy, it is simple to setup and more than enough for my needs. Recently however I noticed that some clients were experiencing unexpected long delays when doing something as simple as curl google.com. After some sleuthing, I tracked it down to the fact I had

LogLevel Info

in my config. When this is set tinyproxy produces log entries such as

CONNECT   Mar 28 08:46:41 [7023]: Connect (file descriptor 7): [unknown] [123.123.123.123]

the [unknown] is due to tinyproxy attempting and failing to resolve the connecting IP into a hostname. It is this attempt to map IP to hostname that is causing the latencies observed. To resolve this I added offending IP to /etc/hosts

123.123.123.123 some-host-name

with the above entry the latency went away and the log entries now look like

CONNECT   Mar 28 20:39:49 [6879]: Connect (file descriptor 7): some-host-name [123.123.123.123]

Conda and Slow Proxies

The default conda connect timeout is 9.15 seconds (no idea why this is, seems to have been the case from day one). If the proxy is slow, e.g. it is in another country or AWS region, this timeout can be triggered and conda will prematurely close the connection.

To diagnose this issue, attempt 

curl https://repo.anaconda.com/pkgs/main/linux-64/current_repodata.json

with proxies enabled. If that works then increase the connect timeout as follows

conda config --set remote_connect_timeout_secs 30

conda should now be able to update and install new packages etc.

2023-02-04

Installing python package arcgis in conda on Apple Silicon

 In order to install the arcgis package in conda, you have to specify the architecture osx-64 otherwise the package will not be found. This works because MacOS will capable of running x86_64 code on Apple Silicon processors - that is how efficient they are!

 

conda install -c esri/osx-64 arcgis 

In fact to make everything else work you need to add the channels with architecture specified exactly, e.g. conda-forge/osx-64, esri/osx-64 and defaults/osx-64. Otherwise packages will be installed with a mix of architectures and appears not to be able to find each other.

2022-12-26

Build Log: ATTiny13 LED Dimmer

Introduction

Bought some cheap USB driven warm-white LEDs off eBay for lighting display cabinets. Turns out it is just normal LED strips with a simple driver in the USB plug. Wanting to unify control of multiple LED strips into a single point I build a simple PWM based LED driver. It is neither constant current nor constant voltage - for the simple LEDs I have it is Good Enough (tm).

Build Details

Finished product first, this is the controller  mounted on the back of the bookcase where the LED strips are mounted. 5V from a wall plug PSU enters on the left and the LEDs strips are wired in parallel on the right. The single button cycles between on and 3 levels of brightness, with max brightness consuming around 800mA.

With the lid off, the interior layout can be examined. Essentially a single strip board containing all components and socket hardware.

The strip board is very basic: decouple and smoothing capacitor on the top-right; ATTiny13 in the middle in a socket so it can be reprogrammed; BD135 NPN transistor lower middle with base resistor to turn the LEDs on and off; and some resistors detect button press. Could have used the internal pull-up resistors but the construction demanded that the button be wires on the high side.

This project was an opportunity to try a new way of encapsulating projects, namely by using PCB spacer to raise the strip board to top of the enclosure and then constructing a lid to match the button's position. This worked quite well, especially here where the NPN transistor needed some room to dissipate heat.  The spacer can be seen in the following photo. Note the vents built into it which line up with the case vents.


The initial case design had the vents (seen below) built in to reduce printing time, turns out they are also very useful for passive cooling of the NPN transistor. 

In addition to the vents in the case, vents were also added to the lid to aid cooling after observing that the case as a whole was warm to touch. The goal is for the whole system to operate without fail for the ~8hr period it is designed to operate for before turning itself off due to idle timeout.

Code and CAD

The ATTiny13 was programmed using AVR ISPmkII, PlatformIO and MicroCore. FreeCAD was used to design the enclosure, using the spreadsheet workflow to parameterise dimensions such as case thickness, inteference-fit-tolerance, etc. 

To Improve

  1. Clean up the vents in the case, possibly increase their size and the thickness of the case in that area so it is less flimsy.
  2. Engrave into the case the LED and 5V labels.
  3. Screw terminals would have been easier to wire up.