-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfeed.xml
244 lines (151 loc) · 36.9 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Quiet Project</title>
<description>Transmit data with sound. Quiet Project offers the ability to build native binaries that work with your soundcard and a JS implementation that uses Web Audio
</description>
<link>https://quiet.github.io/</link>
<atom:link href="https://quiet.github.io/feed.xml" rel="self" type="application/rss+xml"/>
<pubDate>Mon, 17 Sep 2018 02:33:59 -0700</pubDate>
<lastBuildDate>Mon, 17 Sep 2018 02:33:59 -0700</lastBuildDate>
<generator>Jekyll v3.8.3</generator>
<item>
<title>How Libcorrect Corrects Errors, Part I</title>
<description><p><a href="https://github.com/quiet/libcorrect">Libcorrect</a> is a BSD-licensed library for forward error correction. What this means is that it can be given a payload of data and apply specially chosen redundancy. The payload and redundancy are transmitted together and then sent along a medium that might add errors. Once received, the entire structure is decoded into the original payload. The redundancy allows libcorrect to decode the payload as long as the added errors do not exceed some limit. Because it is BSD-licensed, libcorrect can be used for personal and commercial applications.</p>
<p>This blog post is the first in a series which will explain how libcorrect works and what techniques it uses to decode more quickly. This information will hopefully help others who wish to learn more about FEC, implement their own error correction, or contribute to libcorrect. When I first set out to write this library, I had only a passing familiarity with each of these algorithms and managed to piece together some idea of how they worked. It took me quite a while to research each algorithm and my hope is that this post can help others shortcut past some of this time spent.</p>
<p>Libcorrect currently implements two kinds of error correction, <a href="https://en.wikipedia.org/wiki/Convolutional_code">convolutional codes</a> and <a href="https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction">Reed-Solomon error correction</a>. It uses the <a href="https://en.wikipedia.org/wiki/Viterbi_algorithm">Viterbi algorithm</a> to efficiently decode convolutional codes. Neither of these algorithms would be considered state of the art currently, but both were used extensively previously, with applications including dial-up modems, QR codes and long-range space communications. Convolutional codes are robust against Gaussian white noise – that is, transmission errors which occur randomly and according to a normal distribution. Reed-Solomon applies redundancy to a block of bytes and can repair errors that occur anywhere inside the block, even if all of the errors are contiguous to one another. Combining these techniques gives a powerful basis for transmitting in the presence of noise.</p>
<p>This post will focus on the fundamentals of convolutional codes and the Viterbi algorithm. Part II will examine the techniques used by libcorrect to accelerate convolutional code decoding, which includes both portable optimizations as well as Intel SSE vectorizations. Later posts will cover libcorrect’s use of Reed-Solomon error correction.</p>
<h2 id="convolutional-codes">Convolutional Codes</h2>
<p>There are a wide range of options available for forward error correction. For example, we might choose to take each bit of our message and repeat it 3 times. On the receiving end, we would examine each grouping of 3 bits and pick whichever bit appears in majority. We’d like a scheme that has good resilience to errors while not adding too much overhead to our transmission.</p>
<p>Convolutional codes offer a way to encode information about each bit of our message over multiple bits in the transmission. This method effectively “smears out” each bit with its neighboring bits so that no transmitted bit contains only information about just one message bit. The encoder pushes each bit through a shift register and then transmits the outputs of carefully chosen <em>XOR</em> operations. The decoder recreates the message by simulating possible message bits and measuring errors against the received transmission, then choosing the sequence of message bits with the least error. This method can recover the message even when the transmitted bits are received incorrectly periodically. The following sections will examine in more detail how this technique is implemented.</p>
<h3 id="shift-register">Shift Register</h3>
<p>The shift register is a small piece of memory that can store <em>k</em> bits. It is called a shift register because we add a new bit to one end and each bit after slides down to the next cell. We only write one bit in at a time, but we read all the bits concatenated together. If a 4-bit shift register has the contents <code class="highlighter-rouge">{0, 1, 0, 1}</code>, we will write that as <code class="highlighter-rouge">0101</code>. New bits shift in on the left hand side and are discarded on the right. This conceptual device is at the heart of the convolutional code algorithm.</p>
<figure class="figure">
<img src="/assets/sr_anim.gif" class="figure-img img-fluid img-rounded" alt="Shift Register" />
<figcaption class="figure-caption">Shift Register</figcaption>
</figure>
<h3 id="polynomials">Polynomials</h3>
<p>A polynomial is a series of bitwise <em>XOR</em> operations that computes a parity bit – the polynomial operates in <a href="https://en.wikipedia.org/wiki/GF(2)">GF(2)</a>. These polynomials will return <em>1</em> if an odd number of bits are set and <em>0</em> if an even number of bits are set. The polynomials are run with the contents of the shift register as their input. For example, the polynomial <em>x<sup>2</sup></em> + <em>1</em> computes the bitwise <em>XOR</em> of the third newest bit and newest bits in the shift register, while <em>x<sup>3</sup></em> + <em>x</em> + <em>1</em> would do bitwise <em>XOR</em> of the fourth, second, and first newest bits.</p>
<figure class="figure">
<img src="/assets/poly1_anim.gif" class="figure-img img-fluid img-rounded" alt="x&lt;sup&gt;2&lt;/sup&gt; + 1" />
<figcaption class="figure-caption">x<sup>2</sup> + 1</figcaption>
</figure>
<figure class="figure">
<img src="/assets/poly2_anim.gif" class="figure-img img-fluid img-rounded" alt="x&lt;sup&gt;3&lt;/sup&gt; + x + 1" />
<figcaption class="figure-caption">x<sup>3</sup> + x + 1</figcaption>
</figure>
<p>It is these polynomials that form the basis of our redundancy. For each bit that we will load into the shift register, we will transmit the output of at least two different polynomials. The original message itself is not transmitted. Using more than two polynomials can increase tolerance to noise, but comes at the cost of reducing transmission throughput. The polynomials are chosen carefully to complement each other and increase tolerance to noise.</p>
<h3 id="encoding">Encoding</h3>
<p>Encoding convolutional codes involves taking the message we want to send and feeding it one bit at a time through our shift register. The shift register will initially start with all zeros. Each time we feed in a new bit, we will save the output of each polynomial that we have chosen. We continue this process until the entire message has been fed into the shift register, followed by a sequence of 0s to flush the shift register. Once we have completed this process, we send the interleaved polynomial outputs. The transmitted message does not contain the bits of the original message.</p>
<figure class="figure">
<img src="/assets/encoding.png" class="figure-img img-fluid img-rounded" alt="Encoding Message" />
<figcaption class="figure-caption">Encoding Message</figcaption>
</figure>
<h3 id="decoding">Decoding</h3>
<p>As described in the section on encoding, we’ve been given the outputs of the polynomials, not of the shift register itself. What we want to do is to work backwards from these polynomials to determine the <em>most likely</em> input bit to the shift register that produced the received bits. When the decoding algorithm has finished, we hope to recover the series of input bits that were used to generate the polynomial bits that we received.</p>
<p>At first glance this might seem impossible as there are many bits in the shift register but only 1 bit from each polynomial. The only way we can determine which input bit produced the polynomial bits is by simulating the shift register for each group of polynomial bits that we receive. We measure the error between the expected polynomial outputs for each shift register state and the polynomial bits that we received and accumulate the error for each group of inputs. This works well because the shift register’s current state is closely associated with its state at the previous bit. A shift register with the contents <code class="highlighter-rouge">1001000</code> will contain <code class="highlighter-rouge">B100100</code> when the next bit <em>B</em> is shifted in.</p>
<p>Let’s use the example 4-bit shift register with polynomials <em>x<sup>2</sup></em> + <em>1</em> and <em>x<sup>3</sup></em> + <em>x</em> + <em>1</em> mentioned previously. Our approach for decoding will be this: simulate a 4-bit shift register with contents all zero as we started with in the encoder. Next we simulate the first unknown bit being shifted in, which gives us two new possible shift register states, <code class="highlighter-rouge">1000</code> and <code class="highlighter-rouge">0000</code>.</p>
<p>For each of these two states, we will evaluate both polynomial outputs. We then compare these outputs to the two bits we actually received. For both shift register states, we measure the error between what we’d expect our polynomials to generate and what we actually received. Finally, we record this error total for each state. It might turn out at this point that one state mismatched on both polynomials while the other mismatched on neither, yielding error counts of 2 and 0, respectively.</p>
<figure class="figure">
<img src="/assets/decoding.png" class="figure-img img-fluid img-rounded" alt="Decoding First Bit" />
<figcaption class="figure-caption">Decoding First Bit</figcaption>
</figure>
<p>We continue this process for every grouping of polynomial bits we receive. Every time we simulate new shift register states, we copy over the error total from the previous state. For example, if state <code class="highlighter-rouge">0000</code> had an error of 2, then in the next bit, states <code class="highlighter-rouge">0000</code> and <code class="highlighter-rouge">1000</code> will start with an error of 2. Once we’re out of bits to decode, we choose the sequence of bits which has the smallest accumulated error. This sequence is declared to be the original message.</p>
<figure class="figure">
<img src="/assets/decoding_full.png" class="figure-img img-fluid img-rounded" alt="Decoding Second Bit" />
<figcaption class="figure-caption">Decoding Second Bit</figcaption>
</figure>
<p>This strategy has one major flaw. Every time we want to simulate another bit, we must store twice as many register states as we did in the previous bit. When we started, we needed only simulate <code class="highlighter-rouge">0000</code> and <code class="highlighter-rouge">1000</code>. For the following bit, we needed to simulate <code class="highlighter-rouge">0000</code>, <code class="highlighter-rouge">0100</code>, <code class="highlighter-rouge">1000</code>, and <code class="highlighter-rouge">1100</code>. Once the sequence is longer than the shift register, we still must track all possible states, which means that for a message of length <em>m</em> we will need to track the error count for <em>2<sup>m</sup></em> states. This adds up quickly! Thankfully there is a clever trick which reduces the need to track so many states.</p>
<h3 id="viterbi-algorithm">Viterbi Algorithm</h3>
<p>The Viterbi algorithm is a <a href="https://en.wikipedia.org/wiki/Dynamic_programming">Dynamic programming</a> approach to decoding convolutional codes. This algorithm makes one important observation about the sequence of decoded bits. Once our sequence of bits is longer than the length of the shift register, we can discard unlikely paths. Rather than storing error information about <em>2<sup>m</sup></em> paths, we only need store <em>m</em> * <em>2<sup>k</sup></em> paths (for message length <em>m</em> and shift register length <em>k</em>).</p>
<p>Let’s return to our previous 4-bit shift register. Suppose that we are decoding and have received our 4th set of bits and have calculated all of the error counts for the received bits. We have 16 possible sequences so far, and the next set of bits received will bring us to 32 sequences. Each sequence will track 5 bits, but only 4 bits will actually be used for error calculation. If we carefully compare and select sequences at this step, we can actually eliminate half of the sequences without any loss in ability to decode the message.</p>
<p>Imagine that we have calculated the error for sequences <code class="highlighter-rouge">0110</code> and <code class="highlighter-rouge">0111</code>. In the next step, the rightmost bit will shift out of the shift register and will no longer make any contribution to the error count, but we still need to keep track of it to know which bit was transmitted when we finish decoding. Both of these sequences will appear to shift to <code class="highlighter-rouge">B011</code> in the next step, where <em>B</em> is the next bit transmitted. It turns out that we can simply discard whichever of these sequences has a larger error at this point and then record the rightmost bit from the “winning” sequence.</p>
<figure class="figure">
<img src="/assets/viterbi.png" class="figure-img img-fluid img-rounded" alt="Picking a sequence" />
<figcaption class="figure-caption">Picking a sequence</figcaption>
</figure>
<p>For every sequence at this step, there is a complementary sequence we can compare it to. Specifically, the sequences <code class="highlighter-rouge">XYZ0</code> and <code class="highlighter-rouge">XYZ1</code> will be compared and one chosen as a winner. We then store the rightmost bit of the sequence with the smaller error in a table. Once we have finished this step, we will have a table with one bit for each sequence of length <em>k - 1</em>. We will repeat this step for every new group of inputs. Libcorrect calls this table a “history table.”</p>
<p>Once we have finished decoding the message, we will actually work backwards to recover our message. We will start by inspecting which shift register state has the smallest total accumulated error and declare it to be our sequence. We will then shift this state backwards once and use this value to lookup the next bit in our history table. We will <em>OR</em> this bit back into the register and then shift again, repeating the process until we have rewound the entire history table.</p>
<p>You might be wondering whether we could simply choose the sequence with the smallest error amongst all sequences and store only that sequence, rather than storing an entire table. Although this is one possible strategy, it will not yield the same error correction robustness as storing all paths. If we encounter a short-lived burst of errors, it may adversely influence the error count for the correct sequence – remember that we are choosing the sequence which is most likely given the information we have. If we wait until the message has finished and then recover the bits, we are more likely to converge to the correct sequence.</p>
<p>In practice, it is common to do some hybrid approach. The table can store some multiple of <em>k</em> time shifts of sequences, with a periodic “rewind” operation clearing space for new sequences and decoding part of the message. For example, we might wait until we’ve decoded <em>20 * k</em> groups of inputs, and then decode the oldest <em>15 * k</em> bits, leaving the rest for more convergence. Letting the table grow larger uses more memory but requires less CPU time.</p>
<h3 id="demodulation">Demodulation</h3>
<p>When we send data across a network, we usually think of it as being received as a binary signal. The final part of the receiver might be a program that gets bytes of data from a socket. At a lower level, for certain kinds of lossy networks, the data might actually be transmitted as an analog signal. This implies that some part of the receive chain has to convert the received signal back to a digital signal. This component could be the software defined radio on a wireless chipset or even an analog-to-digital sampler on a soundcard.</p>
<p>Often our receivers initially get this signal with some analog fidelity. The code that demodulates the signal can produce a ‘soft’ bit which encodes the demodulator’s confidence in the demodulation process. For example, the demodulator might produce an 8-bit confidence value for every single bit received, where <code class="highlighter-rouge">00000000</code> implies a high level of confidence in a <em>0</em> bit, <code class="highlighter-rouge">11111111</code> implies a high level of confidence in a <em>1</em> bit, and <code class="highlighter-rouge">10000000</code> implies complete uncertainty in which bit was received. This confidence value is useful during error correction because we will want to aggregate our decoding process over a long sequence of bits. Receiving a high level of uncertainty about a single incorrect bit from the demodulator helps the decoder make the right decisions for the other nearby bits.</p>
<h3 id="decoding-soft-bits">Decoding Soft Bits</h3>
<p>We can make use of the demodulator’s soft bits during the convolutional code decoder process. If we run this process on soft bits rather than hard bits, we can continue to recover the message in the presence of more noise than we would have been able to otherwise.</p>
<p>Decoding convolutional codes with soft bits is almost exactly the same process as with hard bits. Previously we calculated the error as the number of bits which differed between the received bits and the simulated polynomial outputs. With soft received bits, we will instead store the absolute value of the difference between the received soft value and the simulated polynomial value encoded as soft bits. For example, if the polynomial value is <em>1</em> and the received soft value is <code class="highlighter-rouge">1110000</code> then we store <code class="highlighter-rouge">11111111 - 11100000 = 11111</code> as the error. If instead the polynomial value were <em>0</em> and the received soft value were <code class="highlighter-rouge">10100000</code> then the error would be <code class="highlighter-rouge">10100000</code>. The error continues to accumulate as a sum just as it does when decoding hard bits.</p>
<h3 id="configuration">Configuration</h3>
<p>We can get more error correction resilience from convolutional codes both by increasing the length of the shift register and by adding more polynomials. Increasing the length of the shift register does not reduce the transmission speed but does cost more CPU resources to decode. Every time we lengthen the shift register by one bit, we will reduce the decoding CPU performance by about 50%. Adding more polynomials adds transmission overhead but has less CPU impact than adding another bit to the shift register.</p>
<p>In practice, common lengths for the shift register range from 7 to 15 bits with 2 to 6 polynomials. Even a modern CPU cannot decode more than 1 million bits per second for a convolutional code with <em>k = 15</em>. These configurations were used for a wide range of error correction including communications with the Voyager probe and the Mars Pathfinder.</p>
<hr />
<p><em>Stay tuned for the next post which will examine how <a href="https://github.com/quiet/libcorrect">libcorrect</a> accelerates the convolutional code decoding process. If you have questions or comments about this post, <a href="https://discordapp.com/invite/eRw5UjF">stop by and say hello</a>.</em></p>
</description>
<pubDate>Sun, 16 Sep 2018 14:25:13 -0700</pubDate>
<link>https://quiet.github.io/quiet-blog/2018/09/16/How-Libcorrect-Does-Forward-Error-Correction.html</link>
<guid isPermaLink="true">https://quiet.github.io/quiet-blog/2018/09/16/How-Libcorrect-Does-Forward-Error-Correction.html</guid>
<category>quiet-blog</category>
</item>
<item>
<title>Generating Swift Documentation From Objective-C</title>
<description><p>I’ve been working on building documentation for <a href="https://github.com/quiet">Quiet Modem Project</a> and I recently came up against a snag in documenting my iOS library that contains Objective-C. Although it’s mostly advisable to stick to writing iOS libraries in Swift these days, I chose Objective-C because it felt nicer when wrapping C libraries. Since the core part of my project is a C library, being able to wrap it cleanly makes life a little easier for me.</p>
<p>The standard I’ve set for my documentation is that I want to show both the code that declares the Class or Function (or some close simplification of it) as well as structured commentary written in plain English. If a function takes two parameters, I want to show the function’s header and a good thorough explanation of what it expects for parameters, what it returns, and any extra notes about how it behaves.</p>
<p>It’s easy enough to programmatically generate API documentation for Objective-C. The wonderful <a href="http://www.doxygen.org">Doxygen</a> is quite capable of parsing structured documentation out of comments. I’m not a fan of the actual pages and stylesheets it generates, but the XML output contains all the relevant documentation, and this output can be used to feed a separate documentation frontend like <a href="https://www.mkdocs.org">MkDocs</a>. The structured output contains something like a structured parsing of the code itself as well as the comment strings I put alongside it. So is this the end of the story?</p>
<p>This method can generate good documentation for Objective-C, but iOS developers are going to want Swift documentation. This means that what we really want to do is generate both the Objective-C docs and the corresponding docs for the translated Swift code. There’s good precedent for this, of course – Apple’s own documentation mostly does this. How would we translate Objective-C documentation into Swift documentation? There are major semantic and syntactic differences between the two languages.</p>
<p>I thought about this for a while and started to feel despair. My project has bindings in JavaScript, Java (Android), Obj-C/Swift and then the original itself in C. One change in the core C library can require updates in documentation for 5 languages. Any solution I came up with would have to be automated, but there was surely no mode for Doxygen to generate Swift documentation from Objective-C.</p>
<p>I had hoped to find that someone else had run into this problem before and had solved it. It was at this point I discovered the <a href="https://github.com/realm/jazzy">jazzy</a> tool. Finding this gave me a lot of optimism that I was going to be able to automate the translation process. Although I couldn’t find a way to get jazzy to do the translation for me, I realized that it relied on a tool called SourceKit that’s supplied by Xcode and that can do some kinds of source code interactions, which I thought might include Objective-C to Swift translations. This made sense to me as I knew that Xcode must somehow have the ability to figure out a Swift header from an Objective-C .h file.</p>
<p>I got <a href="https://github.com/jpsim/SourceKitten">SourceKitten</a> running and started poking around SourceKit’s API. With a bit of Google searching, I found a request that would generate a Swift file from an Objective-C file. I was glad someone else had documented this since I would have never figured it out by myself. Yes, you really do need that UUID, which defines the SourceKit function you’re calling. It’s not localized or specific to your project.</p>
<noscript><pre>400: Invalid request
</pre></noscript>
<script src="https://gist.github.com/c02f20aeadc61ff02ac243bec6a864f5.js"> </script>
<p>Running <code class="highlighter-rouge">sourcekitten request --yaml header.yaml | jq -r '.["key.sourcetext"]' &gt; Foo.swift</code> transforms Foo.h into Foo.swift.</p>
<noscript><pre>400: Invalid request
</pre></noscript>
<script src="https://gist.github.com/39deba2bdfaa812f975b31527a6de5dc.js"> </script>
<noscript><pre>400: Invalid request
</pre></noscript>
<script src="https://gist.github.com/b0fa3a1435c1583049f2fd5c0f71a3f0.js"> </script>
<p>This felt like a bit of magic the first time I saw it. The transformation isn’t always perfect, but it does a pretty good job considering it’s automated. Even our structured comments moved over.</p>
<p>Code translation was an important step forward for what I wanted to do. This still wasn’t enough to build the documentation though, as what I need is the structured code listings and comments that Doxygen and similar tools build. If Doxygen had a Swift mode, this would have been the end of the story. I would just take these translated Swift files and send them through Doxygen. Unfortunately this isn’t the case.</p>
<p>I did know that SourceKit must have something like this capability since tools like jazzy were using it. Generating documentation is thankfully a much more straightforward use of sourcekitten. <code class="highlighter-rouge">sourcekitten structure --file Foo.swift</code> gets us the structured output in a format that’s not so different from Doxygen’s XML output. With some work this can be translated into a nice Markdown file that MkDocs will consume.</p>
<p>Extracting the structured comments is trickier. In Objective-C files, SourceKit actually has some support for pulling structured comments that are in a Javadoc-like style, which is compatible with my Doxygen-style comments. It doesn’t offer the same support for Javadoc-style comments in Swift though as these comments are no longer considered relevant. Instead it is expected that Swift comments are written in Markdown. Even though SourceKit was able to maintain our comments, it can’t actually consume them in a useful manner.</p>
<p>The best option I’ve found is using <code class="highlighter-rouge">sourcekitten doc --single-file Foo.swift</code> to get the block of comments that is associated with each declaration. I believe the easiest option to turn these comment blocks into structured documentation is writing a parser specifically for this task. Thankfully this is considerably easier than extracting the structured code information. This allows me to get the plain English explanation of each function parameter displayed properly alongside the function declaration.</p>
<p>With all of these tools in hand, it is finally possible to generate proper structured documentation for Swift users from Objective-C code. Although I’m sure there are many who would tell me to just give up Objective-C and wrap my libraries in Swift, I’m happy to say that a path to automated documentation does exist for stubborn programmers like myself.</p>
</description>
<pubDate>Mon, 13 Aug 2018 00:25:10 -0700</pubDate>
<link>https://quiet.github.io/quiet-blog/2018/08/13/Objective-C-Swift-Documentation.html</link>
<guid isPermaLink="true">https://quiet.github.io/quiet-blog/2018/08/13/Objective-C-Swift-Documentation.html</guid>
<category>quiet-blog</category>
</item>
<item>
<title>Quiet Profile Lab — Build a Modem, Learn Some DSP</title>
<description><p><a href="https://github.com/quiet/quiet-js">Quiet.js</a> and <a href="https://github.com/quiet/quiet">libquiet</a> are capable of transmission via audible tones, ultrasonic tones, and through an audio cable at wide spectrum. Quiet provides a JSON file which provides parameters for each of these modes. A single set of parameters, a profile, sets the center frequency of the modem, the modulation, error correction modes used, and more. Creating a new profile in a way that’s robust to hardware limitations and yet provides good throughput can be difficult, which is why I’m now pleased to announce the <a href="https://quiet.github.io/quiet-js/lab.html">Quiet Profile Lab</a>, a fully interactive workbench for creating and testing new profiles.</p>
<figure class="figure">
<img src="/images/qpl.png" class="figure-img img-fluid img-rounded" alt="Quiet Profile Lab in action" />
<figcaption class="figure-caption">Quiet Profile Lab in action</figcaption>
</figure>
<p>The <a href="https://quiet.github.io/quiet-js/lab.html">Quiet Profile Lab</a> makes it easy to test out new ideas for profiles. It offers spectrum and constellation diagrams in real time, as well as statistics about throughput, performance, and error rate. This makes it suitable not only for creating new profiles but also learning about modem design. If you’re working from a laptop, you have the perfect testbench – the laptop’s mic will likely pick up the audio generated by its own speakers.</p>
<p>If you are unfamiliar with the techniques used by modems, I welcome you to try out the Lab and experiment with different settings. Because it’s interactive, the Lab is a great way to examine the behavior of various modulation modes. While Wikipedia offers good explanations for many DSP terms, it can be beneficial to see them working in a live example. Even if many of the terms in the Lab are unfamiliar, you may be able to figure out what they do, just by experimenting with them.</p>
<p>If you’re not sure where to start, here are some ideas. Try changing the center frequency. Do you notice changes in how the modem sounds? Reduce the gain and see how quiet you can get the modem while still receiving frames. Increase the interpolation factor (samples per symbol) to narrow the part of the frequency spectrum that your modem uses. Can you find a high frequency, narrow spectrum setting that transfers data but which you can’t hear?</p>
<p>If you have the cable on hand, I highly recommend creating a loopback setup to test on, as well. Passing the audio over a cable greatly reduces noise and will allow you to pass a more delicate, higher throughput signal and will preserve the constellation more than using speakers/mic will. The Lab includes some presets to get you started in cable mode.</p>
<p>Once you’re done, have a look at some of the standards used by the devices you own, like <a href="http://electronicdesign.com/4g/introduction-lte-advanced-real-4g#%E2%80%9DFrequency%E2%80%9D">4g LTE</a> or <a href="https://en.wikipedia.org/wiki/Orthogonal_frequency-division_multiplexing#OFDM_system_comparison_table">802.11a</a>. The Lab provides a good way to get familiar with some of the techniques used in common radios, which are remarkably similar despite being carried by electromagnetic waves rather than by sound.</p>
<p>If you find a profile that you’d like to start using with <a href="https://github.com/quiet/quiet-js">Quiet.js</a>, the Lab provides you with the properly formatted JSON text for the profile you’ve created. It’s as simple as copying the text and pasting it into quiet-profiles.json under a new key.</p>
<p>If you’ve always wanted to learn about DSP but have not had the chance, I hope you’ll spend some time in the <a href="https://quiet.github.io/quiet-js/lab.html">Lab</a>. I think you’ll really like it.</p>
</description>
<pubDate>Wed, 30 Mar 2016 09:30:20 -0700</pubDate>
<link>https://quiet.github.io/quiet-blog/2016/03/30/quiet-profile-lab-build-modem-learn-dsp.html</link>
<guid isPermaLink="true">https://quiet.github.io/quiet-blog/2016/03/30/quiet-profile-lab-build-modem-learn-dsp.html</guid>
<category>quiet-blog</category>
</item>
<item>
<title>Quiet</title>
<description><p>When I started working on <a href="https://github.com/quiet/quiet">libquiet</a>, I was trying to answer a question for myself. I had seen projects which passed data through the headphone jack, which I thought was an interesting idea. I wanted to know how fast this method could send data. Many of these methods used Frequency-Shift Keying, which is easy to implement but typically does not achieve the maximum speed possible. I started researching, which lead me to <a href="http://liquidsdr.org/">liquid sdr</a> which offers basic framing and all the modulation and error correction methods I would need to answer my question. And so, libquiet was born, creating a configurable modem engine which connects soundcard to liquid SDR.</p>
<p>As the project continued, I realized it would also be possible to compile my library to JS using emscripten. I was surprised how well this works, and now, Quiet.js is compatible with the native binaries create with libquiet. My aim is to bring quiet to as many platforms as possible. It’s not just for your headphone jack, either. Quiet works quite well through your speakers.</p>
<p>There’s a few reasons I chose the name quiet. For one, I was thinking about using the headphone jack, which wouldn’t emit any sound. As the project expanded, I realized that ultrasonic transmission would also be possible – another type of quiet modem. Additionally, the quiet modem uses SDR, but it’s “quiet” in the RF spectrum (mostly!). And finally, the best reason, because modems don’t work when they’re clipping! Turn down your volume before using quiet.</p>
</description>
<pubDate>Tue, 29 Mar 2016 11:30:45 -0700</pubDate>
<link>https://quiet.github.io/quiet-blog/2016/03/29/quiet.html</link>
<guid isPermaLink="true">https://quiet.github.io/quiet-blog/2016/03/29/quiet.html</guid>
<category>quiet-blog</category>
</item>
</channel>
</rss>