03/09/2019 at 15:52 •
ObsPy is a collection of seismological programs in Python (2 and 3) that aid in recovering streaming data for analysis, filtering and display of the data. The wiki page is here. ObsPy runs on nearly all modern OS platforms: Windoze, Mac, Linux. It can be installed on a Raspberry Pi with a simple command:
sudo apt-get install python3-obspy
It is much more difficult to get it running on a Mac. Embarrassingly, I have forgotten how I got it running on my Mac desktop machine so I'm no help there (it was either MacPorts or Brew).
I use ObsPy because I find it flexible and better documented than the other public domain seismology programs available in the wild. The filtering capabilities don't generate wild group delay distortion or strange transient effects like I've seen in other programs. This is important to me since the seismometer streams raw data from the cap2dig converter and it needs some post processing to get rid of temperature and drift artifacts.
The Raspberry Pi B+ that is attached to the seismometer is not powerful enough to do much more than manage the cap2dig converter and the ringserver. The CPU usage hovers around 40-50%. I tried to have it archive the data from the ringserver on a daily basis, but it bogged down the machine so badly that the ringserver failed. So now I have a separate Raspberry Pi 2 sitting on the network whose sole job is to archive the daily data stream generated by the seismometer. This is handy since the archival process happens whether or not my desktop machine is running. Every so often I transfer the archived data files to my desktop machine and can view the data anytime after that. It would be tempting to just increase the size of the ringserver buffer, but then the size of the buffer file gets too unwieldy for the ringserver.
The archiving program is seedlinkArchiver.py and is located in the files section of this project. It is managed as a service under systemd and starts upon boot up. I found that it is better to obtain short, 15 minute long, data snippets from the ringserver to avoid the higher probability of getting server errors with long data segments. The archiver checks the time every 10 minutes. If a new UTC day ends then it collects the previous day's data stream from the ringserver. The stream is collected in 15 minute chunks, which the user can lengthen or shorten. If there are gaps in the stream longer than 1 second, or there is a communication error, the program will try three times to get good data. Each chunk of the stream is then concatenated with the others to obtain the entire day's stream. Believe it or not, this approach is faster than just getting the entire day's stream in one long piece. The day's stream then high-pass and/or low-pass filtered to user adjustable values and then archived to a usb thumb drive in both filtered and raw data formats.
Viewing the Data:
There are two basic programs to view the data: directly from the ringserver over the network, or via the archived data streams.
seedlinkRemote.py is a program to view data over the network. (It is possible to get real-time data using a program called seedlink-plotter.py, but it can't really deal with raw seismometer data that contains a significant DC count offset.) I use seedlinkRemote when I wish to view quake events that have not been archived yet. The program has three options: a dayPlot, hourPlot, or custom plot. A calendar widget allows the user to pick a date and then offers the three choices. If the user chooses a dayPlot, he has the option of filtered/raw data, and the magnitude threshold of the event notation. A typical dayPlot with filtered data, and >5 magnitude quakes noted, looks like this:
Pretty boring here in Colorado. We have very stable earth.
The hourPlot only allows for filtered or raw option (the junk in the middle is probably wind noise):
Custom plots are good for isolating a single event or getting some unique data. A custom plot will take user inputs of starting date/hour/minute and the duration of the plot along with filtered or raw as another option:
The above custom plot is raw data over a three+ day period (from the archive). You can see the magnitude of the daily temperature effect and its multi-day trend.
The program for viewing the archived data is ArchivePlotter.py. It offers the same options as seedlinkRemote.py but is much faster because the data is stored on the hard drive. The ArchivePlotter also has access to much more data since the ringserver only has access to the last few days.
Other ObsPy Capabilities:
You can also write programs that automatically detect earthquakes -- my program gives me too many false positives. I've only scratched the surface of ObsPy. Check out the tutorials to get an appreciation for what you can do with it.
02/23/2019 at 05:36 •
A few days ago I replaced the homemade displacement sensor with a shiny new set of PCBs for the rotor and stator plates. It's been pretty quiet out there in the world lately, but today there was a rather large event in Ecuador -- magnitude 7.5 near the border with Peru. The quake was captured by the seismometer.
Unfortunately, there was another spurious event recorded at 18:48UTC. This kind of stuff happens when you disturb the seismometer (or it was an insect crawling around on the boom) -- and it takes a few days for the mechanism to settle out. In any case it appears that the new sensor is working well.
This event is about 6400km from my place in Colorado. Previously, I estimated that 1 count of the cap2dig converter was equivalent to about 0.3nm of displacement. This quake caused the floor of my garage to move about 3µm.
Here's a stripline plot of the same event (added text is mine).
This is what the Z-axis seismometer in Albuquerque, NM picked up:
I might need to adjust the filter parameters a bit to pick up the lower frequency energy...but this is what the Z-axis seismometer picked up in Pinedale, WY:
and it's pretty close to what I got.
02/18/2019 at 13:18 •
I will make all of these programs available to download from the files section of this project or link to the webpage for download. I need to do a bit of cleanup and improve the documentation on my stuff first.
The capacitance to digital converter is not listed among the supported data converters for the popular seismic software packages -- Earthworm, et al. I had to write a custom program to extract the data from the cap2dig converter and massage it into a form that the standard seismic server programs would understand. Here's the flow:
The cap2dig converter uses I2C to interface to the Raspberry Pi computer. It runs in continuous mode -- sampling at 16.2Hz -- and interrupts the Pi on GPIO4 when each conversion is done. The data is collected by a Python program, seismo2mseed.py. The program creates two arrays: the resultArray for the realtime web server, and the seedArray for the seismic data stream server.
When the seedArray reaches 512 samples it is packed into an ascii file and fed to a program called ascii2mseed, which creates another file containing the miniseed data packet.
Lastly, a daemon program called ringserver watches the directory containing the miniseed files and adds the data packets to the data stream that it serves on port 18000. At this point the data stream is available to any computer running standard seismic software -- Earthworm, Quake, ObsPy, etc.
I use ObsPy running on a desktop iMac to view the data in several formats. ObsPy is a collection of Python programs specific to seismic data manipulation.
The heart of this program is a function called getData(). It is called when the cap2dig converter signals that a sample is ready by a falling edge on GPIO4. There is also a Class called AD7745 which contains all of the methods to configure and extract data from the cap2dig converter. The AD7745 requires sequential reads of the I2C port that the standard python smbus libraries don't support very well, so I'm using the Quick2Wire libraries instead. The Quick2Wire stuff is very old, but still works fine in Python3.
A timestamp is added to the data and it is appended to both the resultArray and the seedArray.
When the length of the seedArray is 512 samples the data is packed into a simple text file with this format (explained in the next section), and ascii2mseed is called in the background:
TIMESERIES EI_AEGI__BHZ, 512 samples, 16.2 sps, 2019-02-18T13:47:12.273422, SLIST, INTEGER, Counts -1272258 -1272447 -1272290 -1272276 -1272222 -1272194 -1272313 -1272208 -1272297 -1272342 -1272303 -1272273 -1272373 -1272268 -1272423 -1272324 -1272484 -1272189
The samples are integers representing the the conversion Counts of the cap2dig converter. The integers range from -2^23 to +2^23, or ±8,388,608.
I added a webserver to allow the raw or filtered data to be viewed real-time. I was familiar with Bottle to create and manage a webpage so that is what is used. Bottle can be obtained here.
This is a screenshot of the webpage:
I'm using Flot to plot the data. The data sent to the webpage is decimated 8:1 to reduce loading times. Every 10 seconds or so the data is updated and scrolls. Usually, there is about 1 hour of data to view.
This program is available from IRIS. Simply download it and install on the Raspberry Pi using make. The ascii data file must include a header with the following information:
"TIMESERIES SourceName, # samples, # sps, Time, Format, Type, Units, Headers"
followed by the data in tabbed format. It is pretty well documented in the GitHub repository.
The miniseed file is uniquely named with a timestamp and deposited into a directory on external thumb drive attached to the Raspberry Pi.
This program runs as a daemon under systemd. It performs a seedlink streaming service. I believe it is part of the Earthworm seismic software package (which I could not get to work on my Raspberry Pi.) It is also available from IRIS. Simply download it and install on the Raspberry Pi using make.
In order to run ringserver as a daemon at boot, there must be a ringserver.service file located in /lib/systemd/system/. This is what my ringserver.service looks like:
# This service installs a python script that communicates with the UPS hardware. # It also provides logging to a file # the ringserver.service is located in /lib/systemd/system/ # To test, use sudo systemctl start|stop|status ringserver # To install during the boot process, use: sudo systemctl enable ringserver # If this file gets changed, use: sudo systemctl daemon-reload # If the Python script is changed, use : sudo systemctl restart ringserver [Unit] Description=Powerfail Service Requires=basic.target After=multi-user.target [Service] ExecStart=/usr/local/bin/ringserver /home/pi/ringserver/ring.conf Restart=on-failure RestartSec=10 TimeoutSec=10 # The number of times the service is restarted within a time period can be set # If that condition is met, the RPi can be rebooted # WARNING: # Only use these options with a working system! #StartLimitBurst=4 #StartLimitInterval=180s # actions can be none|reboot|reboot-force|reboot-immidiate #StartLimitAction=reboot # The following are defined the /etc/systemd/system.conf file and are # global for all services # #DefaultTimeoutStartSec=90s #DefaultTimeoutStopSec=90s # # They can also be set on a per process here: # if they are not defined here, they fall back to the system.conf values #TimeoutStartSec=2s #TimeoutStopSec=2s [Install] WantedBy=multi-user.target
The ringserver is configured by a file called ring.conf. I did not make many changes to the ringserver configuration, but I did configure it to watch the directory containing the miniseed files and add them to the data stream. The ring.conf can be downloaded from the files section of this project. You will have to change some paths to get it working in your system.
I created a directory for the ring server: /home/pi/ringserver. In the directory you need to make another directory called "ring". I also put a symbolic link to the location of the miniseed files:
ln -s /media/pi/BPLUSTHUMB/mseed .
The ringserver program will add scan.state into /home/pi/ringserver, and use the /home/pi/ringserver/ring directory for its data files.
The mseed directory is located on a thumb drive. The thumb drive must be formatted to ext4, otherwise the number of files will eventually overload a FAT32 allocation table. To keep the number of files to a reasonable value the seismo2mseed program erases all files more than 2 days old.
01/27/2019 at 16:17 •
At this point, you need to have the seismometer enclosed and the software and hardware working to produce a measurement of displacement. The undamped pendulum should have a natural period of about 2 seconds. This means that if you perturb the pendulum slightly it should "ring" for a long period of time, like this:
I referred to my favorite reference article at this point to calibrate the magnetic damping:
Here's what my test weight looked like when I weighed it:
The thread runs under the peak of the "V" of the paper weight. The feet help to keep it stable for positioning on the boom.
Since this is displacement sensor the response when the weight is lifted from the boom will be a step up in counts -- not a pulse like you would get with a velocity sensor, as per the article. What you are aiming for it something called "critically damped". When you achieve critical damping the frequency response is not peaked and you get a fast settling time from a disturbance. This is the final calibrated response of my seismometer:
What's amazing is the amount of displacement from such a small difference in mass -- nearly 500,000 counts! Note the small overshoot of the response when the weight is removed. I believe it is about 8% of the step height, which yields the same 12:1 overshoot ratio as the reference article. You change the response by moving the magnet frame to cover more or less of the damping blade until you run out of time or patience (just kidding.) But it is a slow process that you only have to perform once.
After I was satisfied with the response I dribbled some epoxy against the base of the magnet frame (without moving it) to hold it in place.
01/26/2019 at 05:03 •
The electronics are the easiest part of this endeavor, thanks to Analog Devices, Inc. The AD7745 IC is a 24-bit fully differential capacitance to digital converter. It is highly integrated, so very few external components are required. Here is the entire circuit schematic.
My original design used a LD2981 3.3V LDO instead of the XC6206. I decided to save a few pennies with the XC6206 since the current loading of the AD7745 is less than 1mA. Total cost of the electronics is about $12.25 -- the majority of which is the AD7745 ($11.28). The circuit board connects to the pin header of the Raspberry Pi, but hangs outside the Raspberry Pi to avoid potential interference problems.
01/26/2019 at 00:00 •
Please forgive me. It's been nearly 4 years since I worked on this problem. A lot of the details have probably been forgotten in the interval. There is a lot of ground to cover, so this might be a very long log entry.
Initially I thought that the way forward was to use a velocity sensor. But subsequent investigation led me to conclude that a displacement sensor was best. I somehow stumbled upon an article by Dr. Randall D. Peters, a professor of Seismology at Mercer University at the time. I lost the original article, but Dr. Peters has written extensively on the subject of using capacitive displacement sensors to detect movement of seismometers, and even patented (U.S. Patent # 5,461,319, Oct. 24, 1995) the concept of a differential capacitance transducer. I will make the patent available in the files section of this project.
Dr. Peter's basic approach was to create a capacitive displacement sensor that was inherently non-contact (therefore no friction), highly linear, differential, and very sensitive. This is a figure from a Dr. Peters paper describing the sensor.
The sensor consist of three plates, which are superimposed on one another: the exciter plate (at the left), the rotor plate (middle) which moves, and the capacitor plate (right). The figure above refers to "linear motion" to simplify the concept. In our case the motion is rotational -- an easy translation.
So what you have here is an exciter plate where the stimulus is applied, a rotor plate that "blocks" the excitation, and a capacitor (static) plate that measures the result via differential means (an amplifier, in this case.) Displacement is measured when the rotor plate moves - it changes the capacitance measured between the excitation (a stator plate) and the capacitance (a stator plate) when the rotor plate changes position.
The rotor plate blocks the signal from the exciter plate to the capacitor plate wherever there is metal. If the rotor is positioned so that it exposes the exciter plates evenly, then the effective capacitance of the sensor is zero. If the rotor plate moves up or down even the slightest amount then the exposed plates of the exciter are unbalanced and the capacitance changes in direct proportion to the movement. Note that the exciter electrode set consists of cross-coupled plates. This arrangement provides for zero displacement when the stator is aligned with the stators, but when the rotor moves it makes the capacitance change (positive or negative) in direct proportion to the displacement.
Unfortunately, the boom is fixed at one end so the displacement is rotational in nature. Fortunately, Dr. Peters provided some answers. He was selling a seismometer that employed two pendulums, x and y, called the Volksmeter, for about $1500. The users manual for the Volksmeter contained a lot of information.
He is giving it all away at this point. I was able to design the two stator plates and the rotor plates from all of the information that he had given away. The patent was granted in 1995. Normal patent life is 20 years, so it has expired as of this writing. Note the disclosure of the AD7745 chip.
The AD7745 is a differential 24-bit capacitance-to-digital converter. It has a maximum differential capacitance capability of about 4pF. It is desired to create a capacitor that will generate nearly ± 4pF for a full scale movement.
So what is a full scale movement? I arbitrarily chose this to be ±5mm. We don't get a lot of seismic activity here in Colorado. There is no need to be able to respond to large displacements. A ±5mm ranges is huge compared to expected incidents. The 5mm range also must include drifting of the mechanical system over time and environmental conditions (temperature, humidity, etc.) The 5mm range sets the width of the capacitor at 10mm.
Next I chose the distance between the rotor and the stator plates to be a small, but doable 1mm. Now the only parameter remaining is the length of the capacitor. I used the spreadsheet below for that. The spreadsheet assumes rectangular geometries to simplify the calculations. Once I got the spreadsheet set up, all that remained was to pick the length and number of capacitors in the array. I chose N=4 to simplify the design (because my initial attempt was using homemade plates.)
The spreadsheet also allowed me to predict the sensitivity of the sensor. The AD7745 published its peak-to-peak noise level, which forms the floor of the response.
Conversion to Rotation:
To convert the design from a linear movement to a rotational movement I had to pick a distance from the hinge to the bottom of the sensor. The current mechanical design set this at 16.5 inches. The length of the sensor was set at 65mm, from the above spreadsheet. Now each slice of plate metal is a wedge that is 63.5mm(2.5") long and 10mm wide at the top. This is the exciter plate.
Below is the capacitor plate.
The two stator plates face each other. The radius R3 at the bottom of the sensor is set by the hinge to sensor distance (16.5"). The radius R1 simply adds 63.5mm to R3 to get the desired sensor capacitance. R2 not the midpoint between R1 and R3. R2 must be set to equalize the areas between the upper and lower plates due to the difference in the inner and outer radii. Therefore:
The rotor plate has vanes to block areas between the stator plates but the vanes begin and end outside the stator plate areas -- allowing the stator blades to define the capacitor area.
01/23/2019 at 05:55 •
This log contains the details of the mechanical stuff -- the dimensions and materials. I will attempt to generate a concise description of how to build the seismometer in the Instructions section. I believe these drawings are accurate. Sorry for the Imperial dimensions, but that's the standard here in the U.S.A.
A large part of the design is simply copied from this article. I highly recommend that you read that article prior building this unit since a lot of it is similar and the detail about assembling the damping unit is identical and explained well . I started out using wooden frame components to keep the cost low while testing out the concepts. I migrated to copper tubing (plumbing pipe and accessories) because I was concerned that the stresses in the wood screws was causing artifacts in the sensor data -- this was probably unfounded. In any case, once you get the frame soldered together there isn't anything that affects it other than temperature.
The critical dimension is the distance from the hinge to the sensor -- this must be 16.5" due to the design of the sensor. It is also desirable to keep the length of the spring close to what is shown here, but spring tension can be adjusted easily by changing the mass and the eyebolt. The placement of the mass should be very close to the point where the spring is attached to the boom to lessen the force on the hinge. Almost everything else can be adjusted during assembly.
Note: I may be changing this log as I document other parts of the project. I'll remove this note when things become more stable.
Above: the side elevation. Below: the front elevation.
Seismometer Mechanical Bill Of Materials:
boom - 4ft x 3/4” aluminum trim channel —(Lowes #215871) $10.49
Springs(2) - Lowes #199346 x 1 - $3.50
24” steel framer's square - Lowes #118056 - $5.97
1 - 22” long 3/4” copper pipe, for the base
2 - 11” long 3/4” copper pipe
1 - 4.125” long 3/4” copper pipe
2 - 3/4” copper 90° elbow
3 - 3/4” copper tee
2 - 1.5” long 3/4” copper pipe to connect tees
2 - 3/4" long 3/4" copper pipe to reinforce tees at ends
11 gauge(?) steel sheet to connect hinge to boom.
4 - 1/8” thick aluminum plates to attach boom, damping, and sensor.
8” x 24” glass plate to rest unit on floor.
3 - 2.5" 1/4x20 machine screw
6 - 1/4x20 nut
3 - 1/4 flat washer
2 - 4" long aluminum trim channel
1 - 1/8" aluminum plate
4 - 1.25" #8/32 machine screws
4 - #8 split washer
4 - #8/32 nut
1 - 14 7/8" long aluminum trim channel
1 - 7/8" long aluminum trim channel (for spring attachment)
3"x4" 22gauge sheet steel
1.25"x1.25"x3/4" wood block
2 - 1/2" #8 sheet metal screw
1 - 1" #6/32 machine screw
2 - #6 flat washer
1 - #6/32 nut
4 - 3/8" #10/32 machine screw
4 - #10/32 nut
1 - 1/2" #10/32 machine screw
1 - #10 split washer
1 - #10/32 nut
1 - empty can of tomato paste
1 - 1.5" 1/4x20 machine screw
3 - 1/4"x20 nut
1 - 1/4" split washer
~400g lead weight
4 - #4/40 x 1.5" screw
8 - #4 flat washer
12 - #4/40 nut
4 - 3.5mm spacers
6 - 0.5" #10/32 machine screw
6 - #10 split washer
6 - #10/32 nut
6 - #10 flat washer
2 - 3/4" copper pipe hanger
1 - 4" long aluminum trim channel
4"x4.5"x1/8" aluminum plate
2.5x4"x1/8" aluminum plate
3 ea. 1.5” x 1/4x20 bolts - round head
9 ea. 1/4x20 nuts
4 ea. Neodymium Magnets
6 ea. 2"x2.75"x1/16" steel plate (use framer's square)
1 ea. 1.5” x 1/4x20 brass bolt
2 ea. 1/4x20 brass nut
1.25"x2" 22-24 gauge copper sheet
4 ea. 1/2" #10/32 machine screw
4 ea. #10/32 nut
4 ea. #10 flat washer
4 ea. #10 split washer
2 ea. 3/4" copper pipe hangers
1 - 4” long 1/4” eyebolt to attach top of spring.
2 - 1/4"x20 nuts for eyebolt